code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <array>
#include <memory>
#include <utility>
#include <vector>

#include <fmt/format.h>

#include "common/microprofile.h"
#include "common/socket_types.h"
#include "core/core.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/sockets/bsd.h"
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/internal_network/network.h"
#include "core/internal_network/socket_proxy.h"
#include "core/internal_network/sockets.h"
#include "network/network.h"

using Common::Expected;
using Common::Unexpected;

namespace Service::Sockets {

namespace {

bool IsConnectionBased(Type type) {
    switch (type) {
    case Type::STREAM:
        return true;
    case Type::DGRAM:
        return false;
    default:
        UNIMPLEMENTED_MSG("Unimplemented type={}", type);
        return false;
    }
}

template <typename T>
T GetValue(std::span<const u8> buffer) {
    T t{};
    std::memcpy(&t, buffer.data(), std::min(sizeof(T), buffer.size()));
    return t;
}

template <typename T>
void PutValue(std::span<u8> buffer, const T& t) {
    std::memcpy(buffer.data(), &t, std::min(sizeof(T), buffer.size()));
}

} // Anonymous namespace

void BSD::PollWork::Execute(BSD* bsd) {
    std::tie(ret, bsd_errno) = bsd->PollImpl(write_buffer, read_buffer, nfds, timeout);
}

void BSD::PollWork::Response(HLERequestContext& ctx) {
    if (write_buffer.size() > 0) {
        ctx.WriteBuffer(write_buffer);
    }

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<s32>(ret);
    rb.PushEnum(bsd_errno);
}

void BSD::AcceptWork::Execute(BSD* bsd) {
    std::tie(ret, bsd_errno) = bsd->AcceptImpl(fd, write_buffer);
}

void BSD::AcceptWork::Response(HLERequestContext& ctx) {
    if (write_buffer.size() > 0) {
        ctx.WriteBuffer(write_buffer);
    }

    IPC::ResponseBuilder rb{ctx, 5};
    rb.Push(ResultSuccess);
    rb.Push<s32>(ret);
    rb.PushEnum(bsd_errno);
    rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}

void BSD::ConnectWork::Execute(BSD* bsd) {
    bsd_errno = bsd->ConnectImpl(fd, addr);
}

void BSD::ConnectWork::Response(HLERequestContext& ctx) {
    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
    rb.PushEnum(bsd_errno);
}

void BSD::RecvWork::Execute(BSD* bsd) {
    std::tie(ret, bsd_errno) = bsd->RecvImpl(fd, flags, message);
}

void BSD::RecvWork::Response(HLERequestContext& ctx) {
    ctx.WriteBuffer(message);

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<s32>(ret);
    rb.PushEnum(bsd_errno);
}

void BSD::RecvFromWork::Execute(BSD* bsd) {
    std::tie(ret, bsd_errno) = bsd->RecvFromImpl(fd, flags, message, addr);
}

void BSD::RecvFromWork::Response(HLERequestContext& ctx) {
    ctx.WriteBuffer(message, 0);
    if (!addr.empty()) {
        ctx.WriteBuffer(addr, 1);
    }

    IPC::ResponseBuilder rb{ctx, 5};
    rb.Push(ResultSuccess);
    rb.Push<s32>(ret);
    rb.PushEnum(bsd_errno);
    rb.Push<u32>(static_cast<u32>(addr.size()));
}

void BSD::SendWork::Execute(BSD* bsd) {
    std::tie(ret, bsd_errno) = bsd->SendImpl(fd, flags, message);
}

void BSD::SendWork::Response(HLERequestContext& ctx) {
    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<s32>(ret);
    rb.PushEnum(bsd_errno);
}

void BSD::SendToWork::Execute(BSD* bsd) {
    std::tie(ret, bsd_errno) = bsd->SendToImpl(fd, flags, message, addr);
}

void BSD::SendToWork::Response(HLERequestContext& ctx) {
    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<s32>(ret);
    rb.PushEnum(bsd_errno);
}

void BSD::RegisterClient(HLERequestContext& ctx) {
    LOG_WARNING(Service, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 3};

    rb.Push(ResultSuccess);
    rb.Push<s32>(0); // bsd errno
}

void BSD::StartMonitoring(HLERequestContext& ctx) {
    LOG_WARNING(Service, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 2};

    rb.Push(ResultSuccess);
}

void BSD::Socket(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const u32 domain = rp.Pop<u32>();
    const u32 type = rp.Pop<u32>();
    const u32 protocol = rp.Pop<u32>();

    LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);

    const auto [fd, bsd_errno] = SocketImpl(static_cast<Domain>(domain), static_cast<Type>(type),
                                            static_cast<Protocol>(protocol));

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<s32>(fd);
    rb.PushEnum(bsd_errno);
}

void BSD::Select(HLERequestContext& ctx) {
    LOG_DEBUG(Service, "(STUBBED) called");

    IPC::ResponseBuilder rb{ctx, 4};

    rb.Push(ResultSuccess);
    rb.Push<u32>(0); // ret
    rb.Push<u32>(0); // bsd errno
}

void BSD::Poll(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 nfds = rp.Pop<s32>();
    const s32 timeout = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);

    ExecuteWork(ctx, PollWork{
                         .nfds = nfds,
                         .timeout = timeout,
                         .read_buffer = ctx.ReadBuffer(),
                         .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
                     });
}

void BSD::Accept(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. fd={}", fd);

    ExecuteWork(ctx, AcceptWork{
                         .fd = fd,
                         .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
                     });
}

void BSD::Bind(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
    BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer()));
}

void BSD::Connect(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());

    ExecuteWork(ctx, ConnectWork{
                         .fd = fd,
                         .addr = ctx.ReadBuffer(),
                     });
}

void BSD::GetPeerName(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. fd={}", fd);

    std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
    const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);

    ctx.WriteBuffer(write_buffer);

    IPC::ResponseBuilder rb{ctx, 5};
    rb.Push(ResultSuccess);
    rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
    rb.PushEnum(bsd_errno);
    rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}

void BSD::GetSockName(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. fd={}", fd);

    std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
    const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);

    ctx.WriteBuffer(write_buffer);

    IPC::ResponseBuilder rb{ctx, 5};
    rb.Push(ResultSuccess);
    rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
    rb.PushEnum(bsd_errno);
    rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}

void BSD::GetSockOpt(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();
    const u32 level = rp.Pop<u32>();
    const auto optname = static_cast<OptName>(rp.Pop<u32>());

    std::vector<u8> optval(ctx.GetWriteBufferSize());

    LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} len=0x{:x}", fd, level, optname,
              optval.size());

    const Errno err = GetSockOptImpl(fd, level, optname, optval);

    ctx.WriteBuffer(optval);

    IPC::ResponseBuilder rb{ctx, 5};
    rb.Push(ResultSuccess);
    rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
    rb.PushEnum(err);
    rb.Push<u32>(static_cast<u32>(optval.size()));
}

void BSD::Listen(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();
    const s32 backlog = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. fd={} backlog={}", fd, backlog);

    BuildErrnoResponse(ctx, ListenImpl(fd, backlog));
}

void BSD::Fcntl(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();
    const s32 cmd = rp.Pop<s32>();
    const s32 arg = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);

    const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<s32>(ret);
    rb.PushEnum(bsd_errno);
}

void BSD::SetSockOpt(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};

    const s32 fd = rp.Pop<s32>();
    const u32 level = rp.Pop<u32>();
    const OptName optname = static_cast<OptName>(rp.Pop<u32>());
    const auto optval = ctx.ReadBuffer();

    LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level,
              static_cast<u32>(optname), optval.size());

    BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
}

void BSD::Shutdown(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};

    const s32 fd = rp.Pop<s32>();
    const s32 how = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. fd={} how={}", fd, how);

    BuildErrnoResponse(ctx, ShutdownImpl(fd, how));
}

void BSD::Recv(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};

    const s32 fd = rp.Pop<s32>();
    const u32 flags = rp.Pop<u32>();

    LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());

    ExecuteWork(ctx, RecvWork{
                         .fd = fd,
                         .flags = flags,
                         .message = std::vector<u8>(ctx.GetWriteBufferSize()),
                     });
}

void BSD::RecvFrom(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};

    const s32 fd = rp.Pop<s32>();
    const u32 flags = rp.Pop<u32>();

    LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
              ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));

    ExecuteWork(ctx, RecvFromWork{
                         .fd = fd,
                         .flags = flags,
                         .message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
                         .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
                     });
}

void BSD::Send(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};

    const s32 fd = rp.Pop<s32>();
    const u32 flags = rp.Pop<u32>();

    LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());

    ExecuteWork(ctx, SendWork{
                         .fd = fd,
                         .flags = flags,
                         .message = ctx.ReadBuffer(),
                     });
}

void BSD::SendTo(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();
    const u32 flags = rp.Pop<u32>();

    LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
              ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));

    ExecuteWork(ctx, SendToWork{
                         .fd = fd,
                         .flags = flags,
                         .message = ctx.ReadBuffer(0),
                         .addr = ctx.ReadBuffer(1),
                     });
}

void BSD::Write(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());

    ExecuteWork(ctx, SendWork{
                         .fd = fd,
                         .flags = 0,
                         .message = ctx.ReadBuffer(),
                     });
}

void BSD::Read(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();

    LOG_WARNING(Service, "(STUBBED) called. fd={} len={}", fd, ctx.GetWriteBufferSize());

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push<u32>(0); // ret
    rb.Push<u32>(0); // bsd errno
}

void BSD::Close(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const s32 fd = rp.Pop<s32>();

    LOG_DEBUG(Service, "called. fd={}", fd);

    BuildErrnoResponse(ctx, CloseImpl(fd));
}

void BSD::DuplicateSocket(HLERequestContext& ctx) {
    struct InputParameters {
        s32 fd;
        u64 reserved;
    };
    static_assert(sizeof(InputParameters) == 0x10);

    struct OutputParameters {
        s32 ret;
        Errno bsd_errno;
    };
    static_assert(sizeof(OutputParameters) == 0x8);

    IPC::RequestParser rp{ctx};
    auto input = rp.PopRaw<InputParameters>();

    Expected<s32, Errno> res = DuplicateSocketImpl(input.fd);
    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.PushRaw(OutputParameters{
        .ret = res.value_or(0),
        .bsd_errno = res ? Errno::SUCCESS : res.error(),
    });
}

void BSD::EventFd(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const u64 initval = rp.Pop<u64>();
    const u32 flags = rp.Pop<u32>();

    LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);

    BuildErrnoResponse(ctx, Errno::SUCCESS);
}

template <typename Work>
void BSD::ExecuteWork(HLERequestContext& ctx, Work work) {
    work.Execute(this);
    work.Response(ctx);
}

std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
    if (type == Type::SEQPACKET) {
        UNIMPLEMENTED_MSG("SOCK_SEQPACKET errno management");
    } else if (type == Type::RAW && (domain != Domain::INET || protocol != Protocol::ICMP)) {
        UNIMPLEMENTED_MSG("SOCK_RAW errno management");
    }

    [[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
    UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
    type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);

    const s32 fd = FindFreeFileDescriptorHandle();
    if (fd < 0) {
        LOG_ERROR(Service, "No more file descriptors available");
        return {-1, Errno::MFILE};
    }

    file_descriptors[fd] = FileDescriptor{};
    FileDescriptor& descriptor = *file_descriptors[fd];
    // ENONMEM might be thrown here

    LOG_INFO(Service, "New socket fd={}", fd);

    auto room_member = room_network.GetRoomMember().lock();
    if (room_member && room_member->IsConnected()) {
        descriptor.socket = std::make_shared<Network::ProxySocket>(room_network);
    } else {
        descriptor.socket = std::make_shared<Network::Socket>();
    }

    descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
    descriptor.is_connection_based = IsConnectionBased(type);

    return {fd, Errno::SUCCESS};
}

std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
                                    s32 nfds, s32 timeout) {
    if (nfds <= 0) {
        // When no entries are provided, -1 is returned with errno zero
        return {-1, Errno::SUCCESS};
    }
    if (read_buffer.size() < nfds * sizeof(PollFD)) {
        return {-1, Errno::INVAL};
    }
    if (write_buffer.size() < nfds * sizeof(PollFD)) {
        return {-1, Errno::INVAL};
    }

    std::vector<PollFD> fds(nfds);
    std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));

    if (timeout >= 0) {
        const s64 seconds = timeout / 1000;
        const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);

        if (seconds < 0) {
            return {-1, Errno::INVAL};
        }
        if (nanoseconds > 999'999'999) {
            return {-1, Errno::INVAL};
        }
    } else if (timeout != -1) {
        return {-1, Errno::INVAL};
    }

    for (PollFD& pollfd : fds) {
        ASSERT(False(pollfd.revents));

        if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
            LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
            pollfd.revents = PollEvents{};
            return {0, Errno::SUCCESS};
        }

        const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
        if (!descriptor) {
            LOG_TRACE(Service, "File descriptor handle={} is not allocated", pollfd.fd);
            pollfd.revents = PollEvents::Nval;
            return {0, Errno::SUCCESS};
        }
    }

    std::vector<Network::PollFD> host_pollfds(fds.size());
    std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) {
        Network::PollFD result;
        result.socket = file_descriptors[pollfd.fd]->socket.get();
        result.events = Translate(pollfd.events);
        result.revents = Network::PollEvents{};
        return result;
    });

    const auto result = Network::Poll(host_pollfds, timeout);

    const size_t num = host_pollfds.size();
    for (size_t i = 0; i < num; ++i) {
        fds[i].revents = Translate(host_pollfds[i].revents);
    }
    std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));

    return Translate(result);
}

std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
    if (!IsFileDescriptorValid(fd)) {
        return {-1, Errno::BADF};
    }

    const s32 new_fd = FindFreeFileDescriptorHandle();
    if (new_fd < 0) {
        LOG_ERROR(Service, "No more file descriptors available");
        return {-1, Errno::MFILE};
    }

    FileDescriptor& descriptor = *file_descriptors[fd];
    auto [result, bsd_errno] = descriptor.socket->Accept();
    if (bsd_errno != Network::Errno::SUCCESS) {
        return {-1, Translate(bsd_errno)};
    }

    file_descriptors[new_fd] = FileDescriptor{};
    FileDescriptor& new_descriptor = *file_descriptors[new_fd];
    new_descriptor.socket = std::move(result.socket);
    new_descriptor.is_connection_based = descriptor.is_connection_based;

    const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
    PutValue(write_buffer, guest_addr_in);

    return {new_fd, Errno::SUCCESS};
}

Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
    if (!IsFileDescriptorValid(fd)) {
        return Errno::BADF;
    }
    ASSERT(addr.size() == sizeof(SockAddrIn));
    auto addr_in = GetValue<SockAddrIn>(addr);

    return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
}

Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
    if (!IsFileDescriptorValid(fd)) {
        return Errno::BADF;
    }

    UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
    auto addr_in = GetValue<SockAddrIn>(addr);

    return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
}

Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
    if (!IsFileDescriptorValid(fd)) {
        return Errno::BADF;
    }

    const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
    if (bsd_errno != Network::Errno::SUCCESS) {
        return Translate(bsd_errno);
    }
    const SockAddrIn guest_addrin = Translate(addr_in);

    ASSERT(write_buffer.size() >= sizeof(guest_addrin));
    write_buffer.resize(sizeof(guest_addrin));
    PutValue(write_buffer, guest_addrin);
    return Translate(bsd_errno);
}

Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
    if (!IsFileDescriptorValid(fd)) {
        return Errno::BADF;
    }

    const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
    if (bsd_errno != Network::Errno::SUCCESS) {
        return Translate(bsd_errno);
    }
    const SockAddrIn guest_addrin = Translate(addr_in);

    ASSERT(write_buffer.size() >= sizeof(guest_addrin));
    write_buffer.resize(sizeof(guest_addrin));
    PutValue(write_buffer, guest_addrin);
    return Translate(bsd_errno);
}

Errno BSD::ListenImpl(s32 fd, s32 backlog) {
    if (!IsFileDescriptorValid(fd)) {
        return Errno::BADF;
    }
    return Translate(file_descriptors[fd]->socket->Listen(backlog));
}

std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
    if (!IsFileDescriptorValid(fd)) {
        return {-1, Errno::BADF};
    }

    FileDescriptor& descriptor = *file_descriptors[fd];

    switch (cmd) {
    case FcntlCmd::GETFL:
        ASSERT(arg == 0);
        return {descriptor.flags, Errno::SUCCESS};
    case FcntlCmd::SETFL: {
        const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
        const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
        if (bsd_errno != Errno::SUCCESS) {
            return {-1, bsd_errno};
        }
        descriptor.flags = arg;
        return {0, Errno::SUCCESS};
    }
    default:
        UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
        return {-1, Errno::SUCCESS};
    }
}

Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
    if (!IsFileDescriptorValid(fd)) {
        return Errno::BADF;
    }

    if (level != static_cast<u32>(SocketLevel::SOCKET)) {
        UNIMPLEMENTED_MSG("Unknown getsockopt level");
        return Errno::SUCCESS;
    }

    Network::SocketBase* const socket = file_descriptors[fd]->socket.get();

    switch (optname) {
    case OptName::ERROR_: {
        auto [pending_err, getsockopt_err] = socket->GetPendingError();
        if (getsockopt_err == Network::Errno::SUCCESS) {
            Errno translated_pending_err = Translate(pending_err);
            ASSERT_OR_EXECUTE_MSG(
                optval.size() == sizeof(Errno), { return Errno::INVAL; },
                "Incorrect getsockopt option size");
            optval.resize(sizeof(Errno));
            PutValue(optval, translated_pending_err);
        }
        return Translate(getsockopt_err);
    }
    default:
        UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
        return Errno::SUCCESS;
    }
}

Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
    if (!IsFileDescriptorValid(fd)) {
        return Errno::BADF;
    }

    if (level != static_cast<u32>(SocketLevel::SOCKET)) {
        UNIMPLEMENTED_MSG("Unknown setsockopt level");
        return Errno::SUCCESS;
    }

    Network::SocketBase* const socket = file_descriptors[fd]->socket.get();

    if (optname == OptName::LINGER) {
        ASSERT(optval.size() == sizeof(Linger));
        auto linger = GetValue<Linger>(optval);
        ASSERT(linger.onoff == 0 || linger.onoff == 1);

        return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
    }

    ASSERT(optval.size() == sizeof(u32));
    auto value = GetValue<u32>(optval);

    switch (optname) {
    case OptName::REUSEADDR:
        ASSERT(value == 0 || value == 1);
        return Translate(socket->SetReuseAddr(value != 0));
    case OptName::KEEPALIVE:
        ASSERT(value == 0 || value == 1);
        return Translate(socket->SetKeepAlive(value != 0));
    case OptName::BROADCAST:
        ASSERT(value == 0 || value == 1);
        return Translate(socket->SetBroadcast(value != 0));
    case OptName::SNDBUF:
        return Translate(socket->SetSndBuf(value));
    case OptName::RCVBUF:
        return Translate(socket->SetRcvBuf(value));
    case OptName::SNDTIMEO:
        return Translate(socket->SetSndTimeo(value));
    case OptName::RCVTIMEO:
        return Translate(socket->SetRcvTimeo(value));
    case OptName::NOSIGPIPE:
        LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
        return Errno::SUCCESS;
    default:
        UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
        return Errno::SUCCESS;
    }
}

Errno BSD::ShutdownImpl(s32 fd, s32 how) {
    if (!IsFileDescriptorValid(fd)) {
        return Errno::BADF;
    }
    const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
    return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
}

std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
    if (!IsFileDescriptorValid(fd)) {
        return {-1, Errno::BADF};
    }

    FileDescriptor& descriptor = *file_descriptors[fd];

    // Apply flags
    using Network::FLAG_MSG_DONTWAIT;
    using Network::FLAG_O_NONBLOCK;
    if ((flags & FLAG_MSG_DONTWAIT) != 0) {
        flags &= ~FLAG_MSG_DONTWAIT;
        if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
            descriptor.socket->SetNonBlock(true);
        }
    }

    const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));

    // Restore original state
    if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
        descriptor.socket->SetNonBlock(false);
    }

    return {ret, bsd_errno};
}

std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
                                        std::vector<u8>& addr) {
    if (!IsFileDescriptorValid(fd)) {
        return {-1, Errno::BADF};
    }

    FileDescriptor& descriptor = *file_descriptors[fd];

    Network::SockAddrIn addr_in{};
    Network::SockAddrIn* p_addr_in = nullptr;
    if (descriptor.is_connection_based) {
        // Connection based file descriptors (e.g. TCP) zero addr
        addr.clear();
    } else {
        p_addr_in = &addr_in;
    }

    // Apply flags
    using Network::FLAG_MSG_DONTWAIT;
    using Network::FLAG_O_NONBLOCK;
    if ((flags & FLAG_MSG_DONTWAIT) != 0) {
        flags &= ~FLAG_MSG_DONTWAIT;
        if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
            descriptor.socket->SetNonBlock(true);
        }
    }

    const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));

    // Restore original state
    if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
        descriptor.socket->SetNonBlock(false);
    }

    if (p_addr_in) {
        if (ret < 0) {
            addr.clear();
        } else {
            ASSERT(addr.size() == sizeof(SockAddrIn));
            const SockAddrIn result = Translate(addr_in);
            PutValue(addr, result);
        }
    }

    return {ret, bsd_errno};
}

std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
    if (!IsFileDescriptorValid(fd)) {
        return {-1, Errno::BADF};
    }
    return Translate(file_descriptors[fd]->socket->Send(message, flags));
}

std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
                                      std::span<const u8> addr) {
    if (!IsFileDescriptorValid(fd)) {
        return {-1, Errno::BADF};
    }

    Network::SockAddrIn addr_in;
    Network::SockAddrIn* p_addr_in = nullptr;
    if (!addr.empty()) {
        ASSERT(addr.size() == sizeof(SockAddrIn));
        auto guest_addr_in = GetValue<SockAddrIn>(addr);
        addr_in = Translate(guest_addr_in);
        p_addr_in = &addr_in;
    }

    return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
}

Errno BSD::CloseImpl(s32 fd) {
    if (!IsFileDescriptorValid(fd)) {
        return Errno::BADF;
    }

    const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
    if (bsd_errno != Errno::SUCCESS) {
        return bsd_errno;
    }

    LOG_INFO(Service, "Close socket fd={}", fd);

    file_descriptors[fd].reset();
    return bsd_errno;
}

Expected<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
    if (!IsFileDescriptorValid(fd)) {
        return Unexpected(Errno::BADF);
    }

    const s32 new_fd = FindFreeFileDescriptorHandle();
    if (new_fd < 0) {
        LOG_ERROR(Service, "No more file descriptors available");
        return Unexpected(Errno::MFILE);
    }

    file_descriptors[new_fd] = file_descriptors[fd];
    return new_fd;
}

std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
    if (!IsFileDescriptorValid(fd)) {
        return std::nullopt;
    }
    return file_descriptors[fd]->socket;
}

s32 BSD::FindFreeFileDescriptorHandle() noexcept {
    for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
        if (!file_descriptors[fd]) {
            return fd;
        }
    }
    return -1;
}

bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
    if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
        LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
        return false;
    }
    if (!file_descriptors[fd]) {
        LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
        return false;
    }
    return true;
}

void BSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
    IPC::ResponseBuilder rb{ctx, 4};

    rb.Push(ResultSuccess);
    rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
    rb.PushEnum(bsd_errno);
}

void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
    for (auto& optional_descriptor : file_descriptors) {
        if (!optional_descriptor.has_value()) {
            continue;
        }
        FileDescriptor& descriptor = *optional_descriptor;
        descriptor.socket.get()->HandleProxyPacket(packet);
    }
}

BSD::BSD(Core::System& system_, const char* name)
    : ServiceFramework{system_, name}, room_network{system_.GetRoomNetwork()} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, &BSD::RegisterClient, "RegisterClient"},
        {1, &BSD::StartMonitoring, "StartMonitoring"},
        {2, &BSD::Socket, "Socket"},
        {3, nullptr, "SocketExempt"},
        {4, nullptr, "Open"},
        {5, &BSD::Select, "Select"},
        {6, &BSD::Poll, "Poll"},
        {7, nullptr, "Sysctl"},
        {8, &BSD::Recv, "Recv"},
        {9, &BSD::RecvFrom, "RecvFrom"},
        {10, &BSD::Send, "Send"},
        {11, &BSD::SendTo, "SendTo"},
        {12, &BSD::Accept, "Accept"},
        {13, &BSD::Bind, "Bind"},
        {14, &BSD::Connect, "Connect"},
        {15, &BSD::GetPeerName, "GetPeerName"},
        {16, &BSD::GetSockName, "GetSockName"},
        {17, &BSD::GetSockOpt, "GetSockOpt"},
        {18, &BSD::Listen, "Listen"},
        {19, nullptr, "Ioctl"},
        {20, &BSD::Fcntl, "Fcntl"},
        {21, &BSD::SetSockOpt, "SetSockOpt"},
        {22, &BSD::Shutdown, "Shutdown"},
        {23, nullptr, "ShutdownAllSockets"},
        {24, &BSD::Write, "Write"},
        {25, &BSD::Read, "Read"},
        {26, &BSD::Close, "Close"},
        {27, &BSD::DuplicateSocket, "DuplicateSocket"},
        {28, nullptr, "GetResourceStatistics"},
        {29, nullptr, "RecvMMsg"},
        {30, nullptr, "SendMMsg"},
        {31, &BSD::EventFd, "EventFd"},
        {32, nullptr, "RegisterResourceStatisticsName"},
        {33, nullptr, "Initialize2"},
    };
    // clang-format on

    RegisterHandlers(functions);

    if (auto room_member = room_network.GetRoomMember().lock()) {
        proxy_packet_received = room_member->BindOnProxyPacketReceived(
            [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); });
    } else {
        LOG_ERROR(Service, "Network isn't initialized");
    }
}

BSD::~BSD() {
    if (auto room_member = room_network.GetRoomMember().lock()) {
        room_member->Unbind(proxy_packet_received);
    }
}

std::unique_lock<std::mutex> BSD::LockService() {
    // Do not lock socket IClient instances.
    return {};
}

BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, nullptr, "SetIfUp"},
        {1, nullptr, "SetIfUpWithEvent"},
        {2, nullptr, "CancelIf"},
        {3, nullptr, "SetIfDown"},
        {4, nullptr, "GetIfState"},
        {5, nullptr, "DhcpRenew"},
        {6, nullptr, "AddStaticArpEntry"},
        {7, nullptr, "RemoveArpEntry"},
        {8, nullptr, "LookupArpEntry"},
        {9, nullptr, "LookupArpEntry2"},
        {10, nullptr, "ClearArpEntries"},
        {11, nullptr, "ClearArpEntries2"},
        {12, nullptr, "PrintArpEntries"},
        {13, nullptr, "Unknown13"},
        {14, nullptr, "Unknown14"},
        {15, nullptr, "Unknown15"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

BSDCFG::~BSDCFG() = default;

} // namespace Service::Sockets


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include <span>
#include <vector>

#include "common/common_types.h"
#include "common/expected.h"
#include "common/socket_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sockets/sockets.h"
#include "network/network.h"

namespace Core {
class System;
}

namespace Network {
class SocketBase;
class Socket;
} // namespace Network

namespace Service::Sockets {

class BSD final : public ServiceFramework<BSD> {
public:
    explicit BSD(Core::System& system_, const char* name);
    ~BSD() override;

    // These methods are called from SSL; the first two are also called from
    // this class for the corresponding IPC methods.
    // On the real device, the SSL service makes IPC calls to this service.
    Common::Expected<s32, Errno> DuplicateSocketImpl(s32 fd);
    Errno CloseImpl(s32 fd);
    std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);

private:
    /// Maximum number of file descriptors
    static constexpr size_t MAX_FD = 128;

    struct FileDescriptor {
        std::shared_ptr<Network::SocketBase> socket;
        s32 flags = 0;
        bool is_connection_based = false;
    };

    struct PollWork {
        void Execute(BSD* bsd);
        void Response(HLERequestContext& ctx);

        s32 nfds;
        s32 timeout;
        std::span<const u8> read_buffer;
        std::vector<u8> write_buffer;
        s32 ret{};
        Errno bsd_errno{};
    };

    struct AcceptWork {
        void Execute(BSD* bsd);
        void Response(HLERequestContext& ctx);

        s32 fd;
        std::vector<u8> write_buffer;
        s32 ret{};
        Errno bsd_errno{};
    };

    struct ConnectWork {
        void Execute(BSD* bsd);
        void Response(HLERequestContext& ctx);

        s32 fd;
        std::span<const u8> addr;
        Errno bsd_errno{};
    };

    struct RecvWork {
        void Execute(BSD* bsd);
        void Response(HLERequestContext& ctx);

        s32 fd;
        u32 flags;
        std::vector<u8> message;
        s32 ret{};
        Errno bsd_errno{};
    };

    struct RecvFromWork {
        void Execute(BSD* bsd);
        void Response(HLERequestContext& ctx);

        s32 fd;
        u32 flags;
        std::vector<u8> message;
        std::vector<u8> addr;
        s32 ret{};
        Errno bsd_errno{};
    };

    struct SendWork {
        void Execute(BSD* bsd);
        void Response(HLERequestContext& ctx);

        s32 fd;
        u32 flags;
        std::span<const u8> message;
        s32 ret{};
        Errno bsd_errno{};
    };

    struct SendToWork {
        void Execute(BSD* bsd);
        void Response(HLERequestContext& ctx);

        s32 fd;
        u32 flags;
        std::span<const u8> message;
        std::span<const u8> addr;
        s32 ret{};
        Errno bsd_errno{};
    };

    void RegisterClient(HLERequestContext& ctx);
    void StartMonitoring(HLERequestContext& ctx);
    void Socket(HLERequestContext& ctx);
    void Select(HLERequestContext& ctx);
    void Poll(HLERequestContext& ctx);
    void Accept(HLERequestContext& ctx);
    void Bind(HLERequestContext& ctx);
    void Connect(HLERequestContext& ctx);
    void GetPeerName(HLERequestContext& ctx);
    void GetSockName(HLERequestContext& ctx);
    void GetSockOpt(HLERequestContext& ctx);
    void Listen(HLERequestContext& ctx);
    void Fcntl(HLERequestContext& ctx);
    void SetSockOpt(HLERequestContext& ctx);
    void Shutdown(HLERequestContext& ctx);
    void Recv(HLERequestContext& ctx);
    void RecvFrom(HLERequestContext& ctx);
    void Send(HLERequestContext& ctx);
    void SendTo(HLERequestContext& ctx);
    void Write(HLERequestContext& ctx);
    void Read(HLERequestContext& ctx);
    void Close(HLERequestContext& ctx);
    void DuplicateSocket(HLERequestContext& ctx);
    void EventFd(HLERequestContext& ctx);

    template <typename Work>
    void ExecuteWork(HLERequestContext& ctx, Work work);

    std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
    std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
                                   s32 nfds, s32 timeout);
    std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
    Errno BindImpl(s32 fd, std::span<const u8> addr);
    Errno ConnectImpl(s32 fd, std::span<const u8> addr);
    Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
    Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
    Errno ListenImpl(s32 fd, s32 backlog);
    std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
    Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
    Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
    Errno ShutdownImpl(s32 fd, s32 how);
    std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
    std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
                                       std::vector<u8>& addr);
    std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
    std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
                                     std::span<const u8> addr);

    s32 FindFreeFileDescriptorHandle() noexcept;
    bool IsFileDescriptorValid(s32 fd) const noexcept;

    void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;

    std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;

    Network::RoomNetwork& room_network;

    /// Callback to parse and handle a received wifi packet.
    void OnProxyPacketReceived(const Network::ProxyPacket& packet);

    // Callback identifier for the OnProxyPacketReceived event.
    Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;

protected:
    virtual std::unique_lock<std::mutex> LockService() override;
};

class BSDCFG final : public ServiceFramework<BSDCFG> {
public:
    explicit BSDCFG(Core::System& system_);
    ~BSDCFG() override;
};

} // namespace Service::Sockets

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/sockets/nsd.h"

#include "common/string_util.h"

namespace Service::Sockets {

constexpr Result ResultOverflow{ErrorModule::NSD, 6};

// This is nn::oe::ServerEnvironmentType
enum class ServerEnvironmentType : u8 {
    Dd,
    Lp,
    Sd,
    Sp,
    Dp,
};

// This is nn::nsd::EnvironmentIdentifier
struct EnvironmentIdentifier {
    std::array<u8, 8> identifier;
};
static_assert(sizeof(EnvironmentIdentifier) == 0x8);

NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {5, nullptr, "GetSettingUrl"},
        {10, nullptr, "GetSettingName"},
        {11, &NSD::GetEnvironmentIdentifier, "GetEnvironmentIdentifier"},
        {12, nullptr, "GetDeviceId"},
        {13, nullptr, "DeleteSettings"},
        {14, nullptr, "ImportSettings"},
        {15, nullptr, "SetChangeEnvironmentIdentifierDisabled"},
        {20, &NSD::Resolve, "Resolve"},
        {21, &NSD::ResolveEx, "ResolveEx"},
        {30, nullptr, "GetNasServiceSetting"},
        {31, nullptr, "GetNasServiceSettingEx"},
        {40, nullptr, "GetNasRequestFqdn"},
        {41, nullptr, "GetNasRequestFqdnEx"},
        {42, nullptr, "GetNasApiFqdn"},
        {43, nullptr, "GetNasApiFqdnEx"},
        {50, nullptr, "GetCurrentSetting"},
        {51, nullptr, "WriteTestParameter"},
        {52, nullptr, "ReadTestParameter"},
        {60, nullptr, "ReadSaveDataFromFsForTest"},
        {61, nullptr, "WriteSaveDataToFsForTest"},
        {62, nullptr, "DeleteSaveDataOfFsForTest"},
        {63, nullptr, "IsChangeEnvironmentIdentifierDisabled"},
        {64, nullptr, "SetWithoutDomainExchangeFqdns"},
        {100, &NSD::GetApplicationServerEnvironmentType, "GetApplicationServerEnvironmentType"},
        {101, nullptr, "SetApplicationServerEnvironmentType"},
        {102, nullptr, "DeleteApplicationServerEnvironmentType"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

static std::string ResolveImpl(const std::string& fqdn_in) {
    // The real implementation makes various substitutions.
    // For now we just return the string as-is, which is good enough when not
    // connecting to real Nintendo servers.
    LOG_WARNING(Service, "(STUBBED) called, fqdn_in={}", fqdn_in);
    return fqdn_in;
}

static Result ResolveCommon(const std::string& fqdn_in, std::array<char, 0x100>& fqdn_out) {
    const auto res = ResolveImpl(fqdn_in);
    if (res.size() >= fqdn_out.size()) {
        return ResultOverflow;
    }
    std::memcpy(fqdn_out.data(), res.c_str(), res.size() + 1);
    return ResultSuccess;
}

void NSD::Resolve(HLERequestContext& ctx) {
    const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0));

    std::array<char, 0x100> fqdn_out{};
    const Result res = ResolveCommon(fqdn_in, fqdn_out);

    ctx.WriteBuffer(fqdn_out);
    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(res);
}

void NSD::ResolveEx(HLERequestContext& ctx) {
    const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0));

    std::array<char, 0x100> fqdn_out;
    const Result res = ResolveCommon(fqdn_in, fqdn_out);

    if (res.IsError()) {
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(res);
        return;
    }

    ctx.WriteBuffer(fqdn_out);
    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(ResultSuccess);
    rb.Push(ResultSuccess);
}

void NSD::GetEnvironmentIdentifier(HLERequestContext& ctx) {
    constexpr EnvironmentIdentifier lp1 = {
        .identifier = {'l', 'p', '1', '\0', '\0', '\0', '\0', '\0'}};
    ctx.WriteBuffer(lp1);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void NSD::GetApplicationServerEnvironmentType(HLERequestContext& ctx) {
    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(static_cast<u32>(ServerEnvironmentType::Lp));
}

NSD::~NSD() = default;

} // namespace Service::Sockets


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::Sockets {

class NSD final : public ServiceFramework<NSD> {
public:
    explicit NSD(Core::System& system_, const char* name);
    ~NSD() override;

private:
    void Resolve(HLERequestContext& ctx);
    void ResolveEx(HLERequestContext& ctx);
    void GetEnvironmentIdentifier(HLERequestContext& ctx);
    void GetApplicationServerEnvironmentType(HLERequestContext& ctx);
};

} // namespace Service::Sockets


code:
// 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, &SFDNSRES::GetGaiStringErrorRequest, "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) {
    const size_t offset = vec.size();
    vec.resize(offset + sizeof(T));
    std::memcpy(vec.data() + offset, &t, sizeof(T));
}

static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
    const size_t offset = vec.size();
    vec.resize(offset + str.size() + 1);
    std::memmove(vec.data() + offset, 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 InputParameters {
        u8 use_nsd_resolve;
        u32 cancel_handle;
        u64 process_id;
    };
    static_assert(sizeof(InputParameters) == 0x10);

    IPC::RequestParser rp{ctx};
    const auto parameters = rp.PopRaw<InputParameters>();

    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.

    // Prevent resolution of Nintendo servers
    if (host.find("srv.nintendo.net") != std::string::npos) {
        LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
        return {0, GetAddrInfoError::AGAIN};
    }

    auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt);
    if (!res.has_value()) {
        return {0, Translate(res.error())};
    }

    const std::vector<u8> data = SerializeAddrInfoAsHostEnt(res.value(), host);
    const 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);

    struct OutputParameters {
        NetDbError netdb_error;
        Errno bsd_errno;
        u32 data_size;
    };
    static_assert(sizeof(OutputParameters) == 0xc);

    IPC::ResponseBuilder rb{ctx, 5};
    rb.Push(ResultSuccess);
    rb.PushRaw(OutputParameters{
        .netdb_error = GetAddrInfoErrorToNetDbError(emu_gai_err),
        .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err),
        .data_size = data_size,
    });
}

void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
    auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx);

    struct OutputParameters {
        u32 data_size;
        NetDbError netdb_error;
        Errno bsd_errno;
    };
    static_assert(sizeof(OutputParameters) == 0xc);

    IPC::ResponseBuilder rb{ctx, 5};
    rb.Push(ResultSuccess);
    rb.PushRaw(OutputParameters{
        .data_size = data_size,
        .netdb_error = GetAddrInfoErrorToNetDbError(emu_gai_err),
        .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err),
    });
}

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 InputParameters {
        u8 use_nsd_resolve;
        u32 cancel_handle;
        u64 process_id;
    };
    static_assert(sizeof(InputParameters) == 0x10);

    IPC::RequestParser rp{ctx};
    const auto parameters = rp.PopRaw<InputParameters>();

    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);

    // Prevent resolution of Nintendo servers
    if (host.find("srv.nintendo.net") != std::string::npos) {
        LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
        return {0, GetAddrInfoError::AGAIN};
    }

    std::optional<std::string> service = std::nullopt;
    if (ctx.CanReadBuffer(1)) {
        const 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::GetAddressInfo(host, service);
    if (!res.has_value()) {
        return {0, Translate(res.error())};
    }

    const std::vector<u8> data = SerializeAddrInfo(res.value(), host);
    const 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);

    struct OutputParameters {
        Errno bsd_errno;
        GetAddrInfoError gai_error;
        u32 data_size;
    };
    static_assert(sizeof(OutputParameters) == 0xc);

    IPC::ResponseBuilder rb{ctx, 5};
    rb.Push(ResultSuccess);
    rb.PushRaw(OutputParameters{
        .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err),
        .gai_error = emu_gai_err,
        .data_size = data_size,
    });
}

void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
    struct InputParameters {
        GetAddrInfoError gai_errno;
    };
    IPC::RequestParser rp{ctx};
    auto input = rp.PopRaw<InputParameters>();

    const std::string result = Translate(input.gai_errno);
    ctx.WriteBuffer(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
    // Additional options are ignored
    auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);

    struct OutputParameters {
        u32 data_size;
        GetAddrInfoError gai_error;
        NetDbError netdb_error;
        Errno bsd_errno;
    };
    static_assert(sizeof(OutputParameters) == 0x10);

    IPC::ResponseBuilder rb{ctx, 6};
    rb.Push(ResultSuccess);
    rb.PushRaw(OutputParameters{
        .data_size = data_size,
        .gai_error = emu_gai_err,
        .netdb_error = GetAddrInfoErrorToNetDbError(emu_gai_err),
        .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err),
    });
}

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


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::Sockets {

class SFDNSRES final : public ServiceFramework<SFDNSRES> {
public:
    explicit SFDNSRES(Core::System& system_);
    ~SFDNSRES() override;

private:
    void GetHostByNameRequest(HLERequestContext& ctx);
    void GetGaiStringErrorRequest(HLERequestContext& ctx);
    void GetHostByNameRequestWithOptions(HLERequestContext& ctx);
    void GetAddrInfoRequest(HLERequestContext& ctx);
    void GetAddrInfoRequestWithOptions(HLERequestContext& ctx);
    void ResolverSetOptionRequest(HLERequestContext& ctx);
};

} // namespace Service::Sockets


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/server_manager.h"
#include "core/hle/service/sockets/bsd.h"
#include "core/hle/service/sockets/nsd.h"
#include "core/hle/service/sockets/sfdnsres.h"
#include "core/hle/service/sockets/sockets.h"

namespace Service::Sockets {

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("bsd:s", std::make_shared<BSD>(system, "bsd:s"));
    server_manager->RegisterNamedService("bsd:u", std::make_shared<BSD>(system, "bsd:u"));
    server_manager->RegisterNamedService("bsdcfg", std::make_shared<BSDCFG>(system));
    server_manager->RegisterNamedService("nsd:a", std::make_shared<NSD>(system, "nsd:a"));
    server_manager->RegisterNamedService("nsd:u", std::make_shared<NSD>(system, "nsd:u"));
    server_manager->RegisterNamedService("sfdnsres", std::make_shared<SFDNSRES>(system));
    server_manager->StartAdditionalHostThreads("bsdsocket", 2);
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::Sockets


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_funcs.h"
#include "common/common_types.h"

namespace Core {
class System;
}

namespace Service::Sockets {

enum class Errno : u32 {
    SUCCESS = 0,
    BADF = 9,
    AGAIN = 11,
    INVAL = 22,
    MFILE = 24,
    PIPE = 32,
    MSGSIZE = 90,
    CONNABORTED = 103,
    CONNRESET = 104,
    NOTCONN = 107,
    TIMEDOUT = 110,
    CONNREFUSED = 111,
    INPROGRESS = 115,
};

enum class GetAddrInfoError : s32 {
    SUCCESS = 0,
    ADDRFAMILY = 1,
    AGAIN = 2,
    BADFLAGS = 3,
    FAIL = 4,
    FAMILY = 5,
    MEMORY = 6,
    NODATA = 7,
    NONAME = 8,
    SERVICE = 9,
    SOCKTYPE = 10,
    SYSTEM = 11,
    BADHINTS = 12,
    PROTOCOL = 13,
    OVERFLOW_ = 14, // avoid name collision with Windows macro
    OTHER = 15,
};

enum class Domain : u32 {
    Unspecified = 0,
    INET = 2,
};

enum class Type : u32 {
    Unspecified = 0,
    STREAM = 1,
    DGRAM = 2,
    RAW = 3,
    SEQPACKET = 5,
};

enum class Protocol : u32 {
    Unspecified = 0,
    ICMP = 1,
    TCP = 6,
    UDP = 17,
};

enum class SocketLevel : u32 {
    SOCKET = 0xffff, // i.e. SOL_SOCKET
};

enum class OptName : u32 {
    REUSEADDR = 0x4,
    KEEPALIVE = 0x8,
    BROADCAST = 0x20,
    LINGER = 0x80,
    SNDBUF = 0x1001,
    RCVBUF = 0x1002,
    SNDTIMEO = 0x1005,
    RCVTIMEO = 0x1006,
    ERROR_ = 0x1007,   // avoid name collision with Windows macro
    NOSIGPIPE = 0x800, // at least according to libnx
};

enum class ShutdownHow : s32 {
    RD = 0,
    WR = 1,
    RDWR = 2,
};

enum class FcntlCmd : s32 {
    GETFL = 3,
    SETFL = 4,
};

struct SockAddrIn {
    u8 len;
    u8 family;
    u16 portno;
    std::array<u8, 4> ip;
    std::array<u8, 8> zeroes;
};

enum class PollEvents : u16 {
    // Using Pascal case because IN is a macro on Windows.
    In = 1 << 0,
    Pri = 1 << 1,
    Out = 1 << 2,
    Err = 1 << 3,
    Hup = 1 << 4,
    Nval = 1 << 5,
    RdNorm = 1 << 6,
    RdBand = 1 << 7,
    WrBand = 1 << 8,
};

DECLARE_ENUM_FLAG_OPERATORS(PollEvents);

struct PollFD {
    s32 fd;
    PollEvents events;
    PollEvents revents;
};

struct Linger {
    u32 onoff;
    u32 linger;
};

void LoopProcess(Core::System& system);

} // namespace Service::Sockets


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <utility>

#include "common/assert.h"
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/internal_network/network.h"

namespace Service::Sockets {

Errno Translate(Network::Errno value) {
    switch (value) {
    case Network::Errno::SUCCESS:
        return Errno::SUCCESS;
    case Network::Errno::BADF:
        return Errno::BADF;
    case Network::Errno::AGAIN:
        return Errno::AGAIN;
    case Network::Errno::INVAL:
        return Errno::INVAL;
    case Network::Errno::MFILE:
        return Errno::MFILE;
    case Network::Errno::PIPE:
        return Errno::PIPE;
    case Network::Errno::CONNREFUSED:
        return Errno::CONNREFUSED;
    case Network::Errno::NOTCONN:
        return Errno::NOTCONN;
    case Network::Errno::TIMEDOUT:
        return Errno::TIMEDOUT;
    case Network::Errno::CONNABORTED:
        return Errno::CONNABORTED;
    case Network::Errno::CONNRESET:
        return Errno::CONNRESET;
    case Network::Errno::INPROGRESS:
        return Errno::INPROGRESS;
    default:
        UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
        return Errno::SUCCESS;
    }
}

std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
    return {value.first, Translate(value.second)};
}

GetAddrInfoError Translate(Network::GetAddrInfoError error) {
    switch (error) {
    case Network::GetAddrInfoError::SUCCESS:
        return GetAddrInfoError::SUCCESS;
    case Network::GetAddrInfoError::ADDRFAMILY:
        return GetAddrInfoError::ADDRFAMILY;
    case Network::GetAddrInfoError::AGAIN:
        return GetAddrInfoError::AGAIN;
    case Network::GetAddrInfoError::BADFLAGS:
        return GetAddrInfoError::BADFLAGS;
    case Network::GetAddrInfoError::FAIL:
        return GetAddrInfoError::FAIL;
    case Network::GetAddrInfoError::FAMILY:
        return GetAddrInfoError::FAMILY;
    case Network::GetAddrInfoError::MEMORY:
        return GetAddrInfoError::MEMORY;
    case Network::GetAddrInfoError::NODATA:
        return GetAddrInfoError::NODATA;
    case Network::GetAddrInfoError::NONAME:
        return GetAddrInfoError::NONAME;
    case Network::GetAddrInfoError::SERVICE:
        return GetAddrInfoError::SERVICE;
    case Network::GetAddrInfoError::SOCKTYPE:
        return GetAddrInfoError::SOCKTYPE;
    case Network::GetAddrInfoError::SYSTEM:
        return GetAddrInfoError::SYSTEM;
    case Network::GetAddrInfoError::BADHINTS:
        return GetAddrInfoError::BADHINTS;
    case Network::GetAddrInfoError::PROTOCOL:
        return GetAddrInfoError::PROTOCOL;
    case Network::GetAddrInfoError::OVERFLOW_:
        return GetAddrInfoError::OVERFLOW_;
    case Network::GetAddrInfoError::OTHER:
        return GetAddrInfoError::OTHER;
    default:
        UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
        return GetAddrInfoError::OTHER;
    }
}

const char* Translate(GetAddrInfoError error) {
    // https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
    switch (error) {
    case GetAddrInfoError::SUCCESS:
        return "Success";
    case GetAddrInfoError::ADDRFAMILY:
        return "Address family for hostname not supported";
    case GetAddrInfoError::AGAIN:
        return "Temporary failure in name resolution";
    case GetAddrInfoError::BADFLAGS:
        return "Invalid value for ai_flags";
    case GetAddrInfoError::FAIL:
        return "Non-recoverable failure in name resolution";
    case GetAddrInfoError::FAMILY:
        return "ai_family not supported";
    case GetAddrInfoError::MEMORY:
        return "Memory allocation failure";
    case GetAddrInfoError::NODATA:
        return "No address associated with hostname";
    case GetAddrInfoError::NONAME:
        return "hostname nor servname provided, or not known";
    case GetAddrInfoError::SERVICE:
        return "servname not supported for ai_socktype";
    case GetAddrInfoError::SOCKTYPE:
        return "ai_socktype not supported";
    case GetAddrInfoError::SYSTEM:
        return "System error returned in errno";
    case GetAddrInfoError::BADHINTS:
        return "Invalid value for hints";
    case GetAddrInfoError::PROTOCOL:
        return "Resolved protocol is unknown";
    case GetAddrInfoError::OVERFLOW_:
        return "Argument buffer overflow";
    default:
        return "Unknown error";
    }
}

Network::Domain Translate(Domain domain) {
    switch (domain) {
    case Domain::Unspecified:
        return Network::Domain::Unspecified;
    case Domain::INET:
        return Network::Domain::INET;
    default:
        UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
        return {};
    }
}

Domain Translate(Network::Domain domain) {
    switch (domain) {
    case Network::Domain::Unspecified:
        return Domain::Unspecified;
    case Network::Domain::INET:
        return Domain::INET;
    default:
        UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
        return {};
    }
}

Network::Type Translate(Type type) {
    switch (type) {
    case Type::Unspecified:
        return Network::Type::Unspecified;
    case Type::STREAM:
        return Network::Type::STREAM;
    case Type::DGRAM:
        return Network::Type::DGRAM;
    case Type::RAW:
        return Network::Type::RAW;
    case Type::SEQPACKET:
        return Network::Type::SEQPACKET;
    default:
        UNIMPLEMENTED_MSG("Unimplemented type={}", type);
        return Network::Type{};
    }
}

Type Translate(Network::Type type) {
    switch (type) {
    case Network::Type::Unspecified:
        return Type::Unspecified;
    case Network::Type::STREAM:
        return Type::STREAM;
    case Network::Type::DGRAM:
        return Type::DGRAM;
    case Network::Type::RAW:
        return Type::RAW;
    case Network::Type::SEQPACKET:
        return Type::SEQPACKET;
    default:
        UNIMPLEMENTED_MSG("Unimplemented type={}", type);
        return Type{};
    }
}

Network::Protocol Translate(Protocol protocol) {
    switch (protocol) {
    case Protocol::Unspecified:
        return Network::Protocol::Unspecified;
    case Protocol::TCP:
        return Network::Protocol::TCP;
    case Protocol::UDP:
        return Network::Protocol::UDP;
    default:
        UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
        return Network::Protocol::Unspecified;
    }
}

Protocol Translate(Network::Protocol protocol) {
    switch (protocol) {
    case Network::Protocol::Unspecified:
        return Protocol::Unspecified;
    case Network::Protocol::TCP:
        return Protocol::TCP;
    case Network::Protocol::UDP:
        return Protocol::UDP;
    default:
        UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
        return Protocol::Unspecified;
    }
}

Network::PollEvents Translate(PollEvents flags) {
    Network::PollEvents result{};
    const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
        if (True(flags & from)) {
            flags &= ~from;
            result |= to;
        }
    };
    translate(PollEvents::In, Network::PollEvents::In);
    translate(PollEvents::Pri, Network::PollEvents::Pri);
    translate(PollEvents::Out, Network::PollEvents::Out);
    translate(PollEvents::Err, Network::PollEvents::Err);
    translate(PollEvents::Hup, Network::PollEvents::Hup);
    translate(PollEvents::Nval, Network::PollEvents::Nval);
    translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
    translate(PollEvents::RdBand, Network::PollEvents::RdBand);
    translate(PollEvents::WrBand, Network::PollEvents::WrBand);

    UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
    return result;
}

PollEvents Translate(Network::PollEvents flags) {
    PollEvents result{};
    const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
        if (True(flags & from)) {
            flags &= ~from;
            result |= to;
        }
    };

    translate(Network::PollEvents::In, PollEvents::In);
    translate(Network::PollEvents::Pri, PollEvents::Pri);
    translate(Network::PollEvents::Out, PollEvents::Out);
    translate(Network::PollEvents::Err, PollEvents::Err);
    translate(Network::PollEvents::Hup, PollEvents::Hup);
    translate(Network::PollEvents::Nval, PollEvents::Nval);
    translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
    translate(Network::PollEvents::RdBand, PollEvents::RdBand);
    translate(Network::PollEvents::WrBand, PollEvents::WrBand);

    UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
    return result;
}

Network::SockAddrIn Translate(SockAddrIn value) {
    // Note: 6 is incorrect, but can be passed by homebrew (because libnx sets
    // sin_len to 6 when deserializing getaddrinfo results).
    ASSERT(value.len == 0 || value.len == sizeof(value) || value.len == 6);

    return {
        .family = Translate(static_cast<Domain>(value.family)),
        .ip = value.ip,
        .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
    };
}

SockAddrIn Translate(Network::SockAddrIn value) {
    return {
        .len = sizeof(SockAddrIn),
        .family = static_cast<u8>(Translate(value.family)),
        .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
        .ip = value.ip,
        .zeroes = {},
    };
}

Network::ShutdownHow Translate(ShutdownHow how) {
    switch (how) {
    case ShutdownHow::RD:
        return Network::ShutdownHow::RD;
    case ShutdownHow::WR:
        return Network::ShutdownHow::WR;
    case ShutdownHow::RDWR:
        return Network::ShutdownHow::RDWR;
    default:
        UNIMPLEMENTED_MSG("Unimplemented how={}", how);
        return {};
    }
}

} // namespace Service::Sockets


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <utility>

#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
#include "core/internal_network/network.h"

namespace Service::Sockets {

/// Translate abstract errno to guest errno
Errno Translate(Network::Errno value);

/// Translate abstract return value errno pair to guest return value errno pair
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);

/// Translate abstract getaddrinfo error to guest getaddrinfo error
GetAddrInfoError Translate(Network::GetAddrInfoError value);

/// Translate guest error to string
const char* Translate(GetAddrInfoError value);

/// Translate guest domain to abstract domain
Network::Domain Translate(Domain domain);

/// Translate abstract domain to guest domain
Domain Translate(Network::Domain domain);

/// Translate guest type to abstract type
Network::Type Translate(Type type);

/// Translate abstract type to guest type
Type Translate(Network::Type type);

/// Translate guest protocol to abstract protocol
Network::Protocol Translate(Protocol protocol);

/// Translate abstract protocol to guest protocol
Protocol Translate(Network::Protocol protocol);

/// Translate guest poll event flags to abstract poll event flags
Network::PollEvents Translate(PollEvents flags);

/// Translate abstract poll event flags to guest poll event flags
PollEvents Translate(Network::PollEvents flags);

/// Translate guest socket address structure to abstract socket address structure
Network::SockAddrIn Translate(SockAddrIn value);

/// Translate abstract socket address structure to guest socket address structure
SockAddrIn Translate(Network::SockAddrIn value);

/// Translate guest shutdown mode to abstract shutdown mode
Network::ShutdownHow Translate(ShutdownHow how);

} // namespace Service::Sockets


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/spl/csrng.h"

namespace Service::SPL {

CSRNG::CSRNG(Core::System& system_, std::shared_ptr<Module> module_)
    : Interface(system_, std::move(module_), "csrng") {
    static const FunctionInfo functions[] = {
        {0, &CSRNG::GenerateRandomBytes, "GenerateRandomBytes"},
    };
    RegisterHandlers(functions);
}

CSRNG::~CSRNG() = default;

} // namespace Service::SPL


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/spl/spl_module.h"

namespace Core {
class System;
}

namespace Service::SPL {

class CSRNG final : public Module::Interface {
public:
    explicit CSRNG(Core::System& system_, std::shared_ptr<Module> module_);
    ~CSRNG() override;
};

} // namespace Service::SPL


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/spl/spl.h"

namespace Service::SPL {

SPL::SPL(Core::System& system_, std::shared_ptr<Module> module_)
    : Interface(system_, std::move(module_), "spl:") {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, &SPL::GetConfig, "GetConfig"},
        {1, &SPL::ModularExponentiate, "ModularExponentiate"},
        {5, &SPL::SetConfig, "SetConfig"},
        {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
        {11, &SPL::IsDevelopment, "IsDevelopment"},
        {24, &SPL::SetBootReason, "SetBootReason"},
        {25, &SPL::GetBootReason, "GetBootReason"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

SPL_MIG::SPL_MIG(Core::System& system_, std::shared_ptr<Module> module_)
    : Interface(system_, std::move(module_), "spl:mig") {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, &SPL::GetConfig, "GetConfig"},
        {1, &SPL::ModularExponentiate, "ModularExponentiate"},
        {2, nullptr, "GenerateAesKek"},
        {3, nullptr, "LoadAesKey"},
        {4, nullptr, "GenerateAesKey"},
        {5, &SPL::SetConfig, "SetConfig"},
        {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
        {11, &SPL::IsDevelopment, "IsDevelopment"},
        {14, nullptr, "DecryptAesKey"},
        {15, nullptr, "CryptAesCtr"},
        {16, nullptr, "ComputeCmac"},
        {21, nullptr, "AllocateAesKeyslot"},
        {22, nullptr, "DeallocateAesKeySlot"},
        {23, nullptr, "GetAesKeyslotAvailableEvent"},
        {24, &SPL::SetBootReason, "SetBootReason"},
        {25, &SPL::GetBootReason, "GetBootReason"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

SPL_FS::SPL_FS(Core::System& system_, std::shared_ptr<Module> module_)
    : Interface(system_, std::move(module_), "spl:fs") {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, &SPL::GetConfig, "GetConfig"},
        {1, &SPL::ModularExponentiate, "ModularExponentiate"},
        {2, nullptr, "GenerateAesKek"},
        {3, nullptr, "LoadAesKey"},
        {4, nullptr, "GenerateAesKey"},
        {5, &SPL::SetConfig, "SetConfig"},
        {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
        {9, nullptr, "ImportLotusKey"},
        {10, nullptr, "DecryptLotusMessage"},
        {11, &SPL::IsDevelopment, "IsDevelopment"},
        {12, nullptr, "GenerateSpecificAesKey"},
        {14, nullptr, "DecryptAesKey"},
        {15, nullptr, "CryptAesCtr"},
        {16, nullptr, "ComputeCmac"},
        {19, nullptr, "LoadTitleKey"},
        {21, nullptr, "AllocateAesKeyslot"},
        {22, nullptr, "DeallocateAesKeySlot"},
        {23, nullptr, "GetAesKeyslotAvailableEvent"},
        {24, &SPL::SetBootReason, "SetBootReason"},
        {25, &SPL::GetBootReason, "GetBootReason"},
        {31, nullptr, "GetPackage2Hash"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

SPL_SSL::SPL_SSL(Core::System& system_, std::shared_ptr<Module> module_)
    : Interface(system_, std::move(module_), "spl:ssl") {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, &SPL::GetConfig, "GetConfig"},
        {1, &SPL::ModularExponentiate, "ModularExponentiate"},
        {2, nullptr, "GenerateAesKek"},
        {3, nullptr, "LoadAesKey"},
        {4, nullptr, "GenerateAesKey"},
        {5, &SPL::SetConfig, "SetConfig"},
        {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
        {11, &SPL::IsDevelopment, "IsDevelopment"},
        {13, nullptr, "DecryptDeviceUniqueData"},
        {14, nullptr, "DecryptAesKey"},
        {15, nullptr, "CryptAesCtr"},
        {16, nullptr, "ComputeCmac"},
        {21, nullptr, "AllocateAesKeyslot"},
        {22, nullptr, "DeallocateAesKeySlot"},
        {23, nullptr, "GetAesKeyslotAvailableEvent"},
        {24, &SPL::SetBootReason, "SetBootReason"},
        {25, &SPL::GetBootReason, "GetBootReason"},
        {26, nullptr, "DecryptAndStoreSslClientCertKey"},
        {27, nullptr, "ModularExponentiateWithSslClientCertKey"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

SPL_ES::SPL_ES(Core::System& system_, std::shared_ptr<Module> module_)
    : Interface(system_, std::move(module_), "spl:es") {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, &SPL::GetConfig, "GetConfig"},
        {1, &SPL::ModularExponentiate, "ModularExponentiate"},
        {2, nullptr, "GenerateAesKek"},
        {3, nullptr, "LoadAesKey"},
        {4, nullptr, "GenerateAesKey"},
        {5, &SPL::SetConfig, "SetConfig"},
        {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
        {11, &SPL::IsDevelopment, "IsDevelopment"},
        {13, nullptr, "DecryptDeviceUniqueData"},
        {14, nullptr, "DecryptAesKey"},
        {15, nullptr, "CryptAesCtr"},
        {16, nullptr, "ComputeCmac"},
        {17, nullptr, "ImportEsKey"},
        {18, nullptr, "UnwrapTitleKey"},
        {20, nullptr, "PrepareEsCommonKey"},
        {21, nullptr, "AllocateAesKeyslot"},
        {22, nullptr, "DeallocateAesKeySlot"},
        {23, nullptr, "GetAesKeyslotAvailableEvent"},
        {24, &SPL::SetBootReason, "SetBootReason"},
        {25, &SPL::GetBootReason, "GetBootReason"},
        {28, nullptr, "DecryptAndStoreDrmDeviceCertKey"},
        {29, nullptr, "ModularExponentiateWithDrmDeviceCertKey"},
        {31, nullptr, "PrepareEsArchiveKey"},
        {32, nullptr, "LoadPreparedAesKey"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

SPL_MANU::SPL_MANU(Core::System& system_, std::shared_ptr<Module> module_)
    : Interface(system_, std::move(module_), "spl:manu") {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, &SPL::GetConfig, "GetConfig"},
        {1, &SPL::ModularExponentiate, "ModularExponentiate"},
        {2, nullptr, "GenerateAesKek"},
        {3, nullptr, "LoadAesKey"},
        {4, nullptr, "GenerateAesKey"},
        {5, &SPL::SetConfig, "SetConfig"},
        {7, &SPL::GenerateRandomBytes, "GenerateRandomBytes"},
        {11, &SPL::IsDevelopment, "IsDevelopment"},
        {13, nullptr, "DecryptDeviceUniqueData"},
        {14, nullptr, "DecryptAesKey"},
        {15, nullptr, "CryptAesCtr"},
        {16, nullptr, "ComputeCmac"},
        {21, nullptr, "AllocateAesKeyslot"},
        {22, nullptr, "DeallocateAesKeySlot"},
        {23, nullptr, "GetAesKeyslotAvailableEvent"},
        {24, &SPL::SetBootReason, "SetBootReason"},
        {25, &SPL::GetBootReason, "GetBootReason"},
        {30, nullptr, "ReencryptDeviceUniqueData"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

SPL::~SPL() = default;

SPL_MIG::~SPL_MIG() = default;

SPL_FS::~SPL_FS() = default;

SPL_SSL::~SPL_SSL() = default;

SPL_ES::~SPL_ES() = default;

SPL_MANU::~SPL_MANU() = default;

} // namespace Service::SPL


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/spl/spl_module.h"

namespace Core {
class System;
}

namespace Service::SPL {

class SPL final : public Module::Interface {
public:
    explicit SPL(Core::System& system_, std::shared_ptr<Module> module_);
    ~SPL() override;
};

class SPL_MIG final : public Module::Interface {
public:
    explicit SPL_MIG(Core::System& system_, std::shared_ptr<Module> module_);
    ~SPL_MIG() override;
};

class SPL_FS final : public Module::Interface {
public:
    explicit SPL_FS(Core::System& system_, std::shared_ptr<Module> module_);
    ~SPL_FS() override;
};

class SPL_SSL final : public Module::Interface {
public:
    explicit SPL_SSL(Core::System& system_, std::shared_ptr<Module> module_);
    ~SPL_SSL() override;
};

class SPL_ES final : public Module::Interface {
public:
    explicit SPL_ES(Core::System& system_, std::shared_ptr<Module> module_);
    ~SPL_ES() override;
};

class SPL_MANU final : public Module::Interface {
public:
    explicit SPL_MANU(Core::System& system_, std::shared_ptr<Module> module_);
    ~SPL_MANU() override;
};

} // namespace Service::SPL


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <vector>
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/hle/api_version.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/spl/csrng.h"
#include "core/hle/service/spl/spl.h"
#include "core/hle/service/spl/spl_module.h"

namespace Service::SPL {

Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
                             const char* name)
    : ServiceFramework{system_, name}, module{std::move(module_)},
      rng(Settings::values.rng_seed_enabled ? Settings::values.rng_seed.GetValue()
                                            : static_cast<u32>(std::time(nullptr))) {}

Module::Interface::~Interface() = default;

void Module::Interface::GetConfig(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto config_item = rp.PopEnum<ConfigItem>();

    // This should call svcCallSecureMonitor with the appropriate args.
    // Since we do not have it implemented yet, we will use this for now.
    u64 smc_result{};
    const auto result_code = GetConfigImpl(&smc_result, config_item);

    if (result_code != ResultSuccess) {
        LOG_ERROR(Service_SPL, "called, config_item={}, result_code={}", config_item,
                  result_code.raw);

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(result_code);
    }

    LOG_DEBUG(Service_SPL, "called, config_item={}, result_code={}, smc_result={}", config_item,
              result_code.raw, smc_result);

    IPC::ResponseBuilder rb{ctx, 4};
    rb.Push(result_code);
    rb.Push(smc_result);
}

void Module::Interface::ModularExponentiate(HLERequestContext& ctx) {
    UNIMPLEMENTED_MSG("ModularExponentiate is not implemented!");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSecureMonitorNotImplemented);
}

void Module::Interface::SetConfig(HLERequestContext& ctx) {
    UNIMPLEMENTED_MSG("SetConfig is not implemented!");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSecureMonitorNotImplemented);
}

void Module::Interface::GenerateRandomBytes(HLERequestContext& ctx) {
    LOG_DEBUG(Service_SPL, "called");

    const std::size_t size = ctx.GetWriteBufferSize();

    std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
    std::vector<u8> data(size);
    std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); });

    ctx.WriteBuffer(data);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void Module::Interface::IsDevelopment(HLERequestContext& ctx) {
    UNIMPLEMENTED_MSG("IsDevelopment is not implemented!");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSecureMonitorNotImplemented);
}

void Module::Interface::SetBootReason(HLERequestContext& ctx) {
    UNIMPLEMENTED_MSG("SetBootReason is not implemented!");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSecureMonitorNotImplemented);
}

void Module::Interface::GetBootReason(HLERequestContext& ctx) {
    UNIMPLEMENTED_MSG("GetBootReason is not implemented!");

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSecureMonitorNotImplemented);
}

Result Module::Interface::GetConfigImpl(u64* out_config, ConfigItem config_item) const {
    switch (config_item) {
    case ConfigItem::DisableProgramVerification:
    case ConfigItem::DramId:
    case ConfigItem::SecurityEngineInterruptNumber:
    case ConfigItem::FuseVersion:
    case ConfigItem::HardwareType:
    case ConfigItem::HardwareState:
    case ConfigItem::IsRecoveryBoot:
    case ConfigItem::DeviceId:
    case ConfigItem::BootReason:
    case ConfigItem::MemoryMode:
    case ConfigItem::IsDevelopmentFunctionEnabled:
    case ConfigItem::KernelConfiguration:
    case ConfigItem::IsChargerHiZModeEnabled:
    case ConfigItem::QuestState:
    case ConfigItem::RegulatorType:
    case ConfigItem::DeviceUniqueKeyGeneration:
    case ConfigItem::Package2Hash:
        return ResultSecureMonitorNotImplemented;
    case ConfigItem::ExosphereApiVersion:
        // Get information about the current exosphere version.
        *out_config = (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MAJOR} << 56) |
                      (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MINOR} << 48) |
                      (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MICRO} << 40) |
                      (static_cast<u64>(HLE::ApiVersion::GetTargetFirmware()));
        return ResultSuccess;
    case ConfigItem::ExosphereNeedsReboot:
        // We are executing, so we aren't in the process of rebooting.
        *out_config = u64{0};
        return ResultSuccess;
    case ConfigItem::ExosphereNeedsShutdown:
        // We are executing, so we aren't in the process of shutting down.
        *out_config = u64{0};
        return ResultSuccess;
    case ConfigItem::ExosphereGitCommitHash:
        // Get information about the current exosphere git commit hash.
        *out_config = u64{0};
        return ResultSuccess;
    case ConfigItem::ExosphereHasRcmBugPatch:
        // Get information about whether this unit has the RCM bug patched.
        *out_config = u64{0};
        return ResultSuccess;
    case ConfigItem::ExosphereBlankProdInfo:
        // Get whether this unit should simulate a "blanked" PRODINFO.
        *out_config = u64{0};
        return ResultSuccess;
    case ConfigItem::ExosphereAllowCalWrites:
        // Get whether this unit should allow writing to the calibration partition.
        *out_config = u64{0};
        return ResultSuccess;
    case ConfigItem::ExosphereEmummcType:
        // Get what kind of emummc this unit has active.
        *out_config = u64{0};
        return ResultSuccess;
    case ConfigItem::ExospherePayloadAddress:
        // Gets the physical address of the reboot payload buffer, if one exists.
        return ResultSecureMonitorNotInitialized;
    case ConfigItem::ExosphereLogConfiguration:
        // Get the log configuration.
        *out_config = u64{0};
        return ResultSuccess;
    case ConfigItem::ExosphereForceEnableUsb30:
        // Get whether usb 3.0 should be force-enabled.
        *out_config = u64{0};
        return ResultSuccess;
    default:
        return ResultSecureMonitorInvalidArgument;
    }
}

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);
    auto module = std::make_shared<Module>();

    server_manager->RegisterNamedService("csrng", std::make_shared<CSRNG>(system, module));
    server_manager->RegisterNamedService("spl", std::make_shared<SPL>(system, module));
    server_manager->RegisterNamedService("spl:mig", std::make_shared<SPL_MIG>(system, module));
    server_manager->RegisterNamedService("spl:fs", std::make_shared<SPL_FS>(system, module));
    server_manager->RegisterNamedService("spl:ssl", std::make_shared<SPL_SSL>(system, module));
    server_manager->RegisterNamedService("spl:es", std::make_shared<SPL_ES>(system, module));
    server_manager->RegisterNamedService("spl:manu", std::make_shared<SPL_MANU>(system, module));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::SPL


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <random>
#include "core/hle/service/service.h"
#include "core/hle/service/spl/spl_results.h"
#include "core/hle/service/spl/spl_types.h"

namespace Core {
class System;
}

namespace Service::SPL {

class Module final {
public:
    class Interface : public ServiceFramework<Interface> {
    public:
        explicit Interface(Core::System& system_, std::shared_ptr<Module> module_,
                           const char* name);
        ~Interface() override;

        // General
        void GetConfig(HLERequestContext& ctx);
        void ModularExponentiate(HLERequestContext& ctx);
        void SetConfig(HLERequestContext& ctx);
        void GenerateRandomBytes(HLERequestContext& ctx);
        void IsDevelopment(HLERequestContext& ctx);
        void SetBootReason(HLERequestContext& ctx);
        void GetBootReason(HLERequestContext& ctx);

    protected:
        std::shared_ptr<Module> module;

    private:
        Result GetConfigImpl(u64* out_config, ConfigItem config_item) const;

        std::mt19937 rng;
    };
};

void LoopProcess(Core::System& system);

} // namespace Service::SPL


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"

namespace Service::SPL {

// Description 0 - 99
constexpr Result ResultSecureMonitorError{ErrorModule::SPL, 0};
constexpr Result ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1};
constexpr Result ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2};
constexpr Result ResultSecureMonitorBusy{ErrorModule::SPL, 3};
constexpr Result ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4};
constexpr Result ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5};
constexpr Result ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6};
constexpr Result ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7};

constexpr Result ResultInvalidSize{ErrorModule::SPL, 100};
constexpr Result ResultUnknownSecureMonitorError{ErrorModule::SPL, 101};
constexpr Result ResultDecryptionFailed{ErrorModule::SPL, 102};

constexpr Result ResultOutOfKeySlots{ErrorModule::SPL, 104};
constexpr Result ResultInvalidKeySlot{ErrorModule::SPL, 105};
constexpr Result ResultBootReasonAlreadySet{ErrorModule::SPL, 106};
constexpr Result ResultBootReasonNotSet{ErrorModule::SPL, 107};
constexpr Result ResultInvalidArgument{ErrorModule::SPL, 108};

} // namespace Service::SPL


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <span>

#include "common/bit_field.h"
#include "common/common_types.h"

namespace Service::SPL {

constexpr size_t AES_128_KEY_SIZE = 0x10;

namespace Smc {

enum class FunctionId : u32 {
    SetConfig = 0xC3000401,
    GetConfig = 0xC3000002,
    GetResult = 0xC3000003,
    GetResultData = 0xC3000404,
    ModularExponentiate = 0xC3000E05,
    GenerateRandomBytes = 0xC3000006,
    GenerateAesKek = 0xC3000007,
    LoadAesKey = 0xC3000008,
    ComputeAes = 0xC3000009,
    GenerateSpecificAesKey = 0xC300000A,
    ComputeCmac = 0xC300040B,
    ReencryptDeviceUniqueData = 0xC300D60C,
    DecryptDeviceUniqueData = 0xC300100D,

    ModularExponentiateWithStorageKey = 0xC300060F,
    PrepareEsDeviceUniqueKey = 0xC3000610,
    LoadPreparedAesKey = 0xC3000011,
    PrepareCommonEsTitleKey = 0xC3000012,

    // Deprecated functions.
    LoadEsDeviceKey = 0xC300100C,
    DecryptAndStoreGcKey = 0xC300100E,

    // Atmosphere functions.
    AtmosphereIramCopy = 0xF0000201,
    AtmosphereReadWriteRegister = 0xF0000002,

    AtmosphereGetEmummcConfig = 0xF0000404,
};

enum class CipherMode {
    CbcEncrypt = 0,
    CbcDecrypt = 1,
    Ctr = 2,
};

enum class DeviceUniqueDataMode {
    DecryptDeviceUniqueData = 0,
    DecryptAndStoreGcKey = 1,
    DecryptAndStoreEsDeviceKey = 2,
    DecryptAndStoreSslKey = 3,
    DecryptAndStoreDrmDeviceCertKey = 4,
};

enum class ModularExponentiateWithStorageKeyMode {
    Gc = 0,
    Ssl = 1,
    DrmDeviceCert = 2,
};

enum class EsCommonKeyType {
    TitleKey = 0,
    ArchiveKey = 1,
};

struct AsyncOperationKey {
    u64 value;
};

} // namespace Smc

enum class HardwareType {
    Icosa = 0,
    Copper = 1,
    Hoag = 2,
    Iowa = 3,
    Calcio = 4,
    Aula = 5,
};

enum class SocType {
    Erista = 0,
    Mariko = 1,
};

enum class HardwareState {
    Development = 0,
    Production = 1,
};

enum class MemoryArrangement {
    Standard = 0,
    StandardForAppletDev = 1,
    StandardForSystemDev = 2,
    Expanded = 3,
    ExpandedForAppletDev = 4,

    // Note: Dynamic is not official.
    // Atmosphere uses it to maintain compatibility with firmwares prior to 6.0.0,
    // which removed the explicit retrieval of memory arrangement from PM.
    Dynamic = 5,
    Count,
};

enum class BootReason {
    Unknown = 0,
    AcOk = 1,
    OnKey = 2,
    RtcAlarm1 = 3,
    RtcAlarm2 = 4,
};

struct BootReasonValue {
    union {
        u32 value{};

        BitField<0, 8, u32> power_intr;
        BitField<8, 8, u32> rtc_intr;
        BitField<16, 8, u32> nv_erc;
        BitField<24, 8, u32> boot_reason;
    };
};
static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");

struct AesKey {
    std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};

    std::span<u8> AsBytes() {
        return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
    }

    std::span<const u8> AsBytes() const {
        return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
    }
};
static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "AesKey definition!");

struct IvCtr {
    std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};

    std::span<u8> AsBytes() {
        return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
    }

    std::span<const u8> AsBytes() const {
        return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
    }
};
static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "IvCtr definition!");

struct Cmac {
    std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};

    std::span<u8> AsBytes() {
        return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
    }

    std::span<const u8> AsBytes() const {
        return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
    }
};
static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "Cmac definition!");

struct AccessKey {
    std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};

    std::span<u8> AsBytes() {
        return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
    }

    std::span<const u8> AsBytes() const {
        return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
    }
};
static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "AccessKey definition!");

struct KeySource {
    std::array<u64, AES_128_KEY_SIZE / sizeof(u64)> data64{};

    std::span<u8> AsBytes() {
        return std::span{reinterpret_cast<u8*>(data64.data()), AES_128_KEY_SIZE};
    }

    std::span<const u8> AsBytes() const {
        return std::span{reinterpret_cast<const u8*>(data64.data()), AES_128_KEY_SIZE};
    }
};
static_assert(sizeof(AesKey) == AES_128_KEY_SIZE, "KeySource definition!");

enum class ConfigItem : u32 {
    // Standard config items.
    DisableProgramVerification = 1,
    DramId = 2,
    SecurityEngineInterruptNumber = 3,
    FuseVersion = 4,
    HardwareType = 5,
    HardwareState = 6,
    IsRecoveryBoot = 7,
    DeviceId = 8,
    BootReason = 9,
    MemoryMode = 10,
    IsDevelopmentFunctionEnabled = 11,
    KernelConfiguration = 12,
    IsChargerHiZModeEnabled = 13,
    QuestState = 14,
    RegulatorType = 15,
    DeviceUniqueKeyGeneration = 16,
    Package2Hash = 17,

    // Extension config items for exosphere.
    ExosphereApiVersion = 65000,
    ExosphereNeedsReboot = 65001,
    ExosphereNeedsShutdown = 65002,
    ExosphereGitCommitHash = 65003,
    ExosphereHasRcmBugPatch = 65004,
    ExosphereBlankProdInfo = 65005,
    ExosphereAllowCalWrites = 65006,
    ExosphereEmummcType = 65007,
    ExospherePayloadAddress = 65008,
    ExosphereLogConfiguration = 65009,
    ExosphereForceEnableUsb30 = 65010,
};

} // namespace Service::SPL