Merge pull request #4306 from ReinUsesLisp/bsd-network
core/network: Add network abstraction
This commit is contained in:
		@@ -586,6 +586,9 @@ add_library(core STATIC
 | 
			
		||||
    memory/dmnt_cheat_vm.h
 | 
			
		||||
    memory.cpp
 | 
			
		||||
    memory.h
 | 
			
		||||
    network/network.cpp
 | 
			
		||||
    network/network.h
 | 
			
		||||
    network/sockets.h
 | 
			
		||||
    perf_stats.cpp
 | 
			
		||||
    perf_stats.h
 | 
			
		||||
    reporter.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
#include "core/memory/cheat_engine.h"
 | 
			
		||||
#include "core/network/network.h"
 | 
			
		||||
#include "core/perf_stats.h"
 | 
			
		||||
#include "core/reporter.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
@@ -394,6 +395,9 @@ struct System::Impl {
 | 
			
		||||
    /// Telemetry session for this emulation session
 | 
			
		||||
    std::unique_ptr<Core::TelemetrySession> telemetry_session;
 | 
			
		||||
 | 
			
		||||
    /// Network instance
 | 
			
		||||
    Network::NetworkInstance network_instance;
 | 
			
		||||
 | 
			
		||||
    ResultStatus status = ResultStatus::Success;
 | 
			
		||||
    std::string status_details = "";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										652
									
								
								src/core/network/network.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										652
									
								
								src/core/network/network.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,652 @@
 | 
			
		||||
// Copyright 2020 yuzu emulator team
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
 | 
			
		||||
#include <winsock2.h>
 | 
			
		||||
#elif __unix__
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <netdb.h>
 | 
			
		||||
#include <poll.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#else
 | 
			
		||||
#error "Unimplemented platform"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/network/network.h"
 | 
			
		||||
#include "core/network/sockets.h"
 | 
			
		||||
 | 
			
		||||
namespace Network {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
 | 
			
		||||
using socklen_t = int;
 | 
			
		||||
 | 
			
		||||
void Initialize() {
 | 
			
		||||
    WSADATA wsa_data;
 | 
			
		||||
    (void)WSAStartup(MAKEWORD(2, 2), &wsa_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Finalize() {
 | 
			
		||||
    WSACleanup();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
constexpr IPv4Address TranslateIPv4(in_addr addr) {
 | 
			
		||||
    auto& bytes = addr.S_un.S_un_b;
 | 
			
		||||
    return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
 | 
			
		||||
    sockaddr_in result;
 | 
			
		||||
 | 
			
		||||
#ifdef __unix__
 | 
			
		||||
    result.sin_len = sizeof(result);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    switch (static_cast<Domain>(input.family)) {
 | 
			
		||||
    case Domain::INET:
 | 
			
		||||
        result.sin_family = AF_INET;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", static_cast<int>(input.family));
 | 
			
		||||
        result.sin_family = AF_INET;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    result.sin_port = htons(input.portno);
 | 
			
		||||
 | 
			
		||||
    auto& ip = result.sin_addr.S_un.S_un_b;
 | 
			
		||||
    ip.s_b1 = input.ip[0];
 | 
			
		||||
    ip.s_b2 = input.ip[1];
 | 
			
		||||
    ip.s_b3 = input.ip[2];
 | 
			
		||||
    ip.s_b4 = input.ip[3];
 | 
			
		||||
 | 
			
		||||
    sockaddr addr;
 | 
			
		||||
    std::memcpy(&addr, &result, sizeof(addr));
 | 
			
		||||
    return addr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LINGER MakeLinger(bool enable, u32 linger_value) {
 | 
			
		||||
    ASSERT(linger_value <= std::numeric_limits<u_short>::max());
 | 
			
		||||
 | 
			
		||||
    LINGER value;
 | 
			
		||||
    value.l_onoff = enable ? 1 : 0;
 | 
			
		||||
    value.l_linger = static_cast<u_short>(linger_value);
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int LastError() {
 | 
			
		||||
    return WSAGetLastError();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool EnableNonBlock(SOCKET fd, bool enable) {
 | 
			
		||||
    u_long value = enable ? 1 : 0;
 | 
			
		||||
    return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#elif __unix__ // ^ _WIN32 v __unix__
 | 
			
		||||
 | 
			
		||||
using SOCKET = int;
 | 
			
		||||
using WSAPOLLFD = pollfd;
 | 
			
		||||
using ULONG = u64;
 | 
			
		||||
 | 
			
		||||
constexpr SOCKET INVALID_SOCKET = -1;
 | 
			
		||||
constexpr SOCKET SOCKET_ERROR = -1;
 | 
			
		||||
 | 
			
		||||
constexpr int WSAEWOULDBLOCK = EAGAIN;
 | 
			
		||||
constexpr int WSAENOTCONN = ENOTCONN;
 | 
			
		||||
 | 
			
		||||
constexpr int SD_RECEIVE = SHUT_RD;
 | 
			
		||||
constexpr int SD_SEND = SHUT_WR;
 | 
			
		||||
constexpr int SD_BOTH = SHUT_RDWR;
 | 
			
		||||
 | 
			
		||||
void Initialize() {}
 | 
			
		||||
 | 
			
		||||
void Finalize() {}
 | 
			
		||||
 | 
			
		||||
constexpr IPv4Address TranslateIPv4(in_addr addr) {
 | 
			
		||||
    const u32 bytes = addr.s_addr;
 | 
			
		||||
    return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
 | 
			
		||||
                       static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
 | 
			
		||||
    sockaddr_in result;
 | 
			
		||||
 | 
			
		||||
    switch (static_cast<Domain>(input.family)) {
 | 
			
		||||
    case Domain::INET:
 | 
			
		||||
        result.sin_family = AF_INET;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", static_cast<int>(input.family));
 | 
			
		||||
        result.sin_family = AF_INET;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    result.sin_port = htons(input.portno);
 | 
			
		||||
 | 
			
		||||
    result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24;
 | 
			
		||||
 | 
			
		||||
    sockaddr addr;
 | 
			
		||||
    std::memcpy(&addr, &result, sizeof(addr));
 | 
			
		||||
    return addr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) {
 | 
			
		||||
    return poll(fds, nfds, timeout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int closesocket(SOCKET fd) {
 | 
			
		||||
    return close(fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
linger MakeLinger(bool enable, u32 linger_value) {
 | 
			
		||||
    linger value;
 | 
			
		||||
    value.l_onoff = enable ? 1 : 0;
 | 
			
		||||
    value.l_linger = linger_value;
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int LastError() {
 | 
			
		||||
    return errno;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool EnableNonBlock(int fd, bool enable) {
 | 
			
		||||
    int flags = fcntl(fd, F_GETFD);
 | 
			
		||||
    if (flags == -1) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    if (enable) {
 | 
			
		||||
        flags |= O_NONBLOCK;
 | 
			
		||||
    } else {
 | 
			
		||||
        flags &= ~O_NONBLOCK;
 | 
			
		||||
    }
 | 
			
		||||
    return fcntl(fd, F_SETFD, flags) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int TranslateDomain(Domain domain) {
 | 
			
		||||
    switch (domain) {
 | 
			
		||||
    case Domain::INET:
 | 
			
		||||
        return AF_INET;
 | 
			
		||||
    default:
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int TranslateType(Type type) {
 | 
			
		||||
    switch (type) {
 | 
			
		||||
    case Type::STREAM:
 | 
			
		||||
        return SOCK_STREAM;
 | 
			
		||||
    case Type::DGRAM:
 | 
			
		||||
        return SOCK_DGRAM;
 | 
			
		||||
    default:
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int TranslateProtocol(Protocol protocol) {
 | 
			
		||||
    switch (protocol) {
 | 
			
		||||
    case Protocol::TCP:
 | 
			
		||||
        return IPPROTO_TCP;
 | 
			
		||||
    case Protocol::UDP:
 | 
			
		||||
        return IPPROTO_UDP;
 | 
			
		||||
    default:
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SockAddrIn TranslateToSockAddrIn(sockaddr input_) {
 | 
			
		||||
    sockaddr_in input;
 | 
			
		||||
    std::memcpy(&input, &input_, sizeof(input));
 | 
			
		||||
 | 
			
		||||
    SockAddrIn result;
 | 
			
		||||
 | 
			
		||||
    switch (input.sin_family) {
 | 
			
		||||
    case AF_INET:
 | 
			
		||||
        result.family = Domain::INET;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family);
 | 
			
		||||
        result.family = Domain::INET;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    result.portno = ntohs(input.sin_port);
 | 
			
		||||
 | 
			
		||||
    result.ip = TranslateIPv4(input.sin_addr);
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u16 TranslatePollEvents(u16 events) {
 | 
			
		||||
    u16 result = 0;
 | 
			
		||||
 | 
			
		||||
    if (events & POLL_IN) {
 | 
			
		||||
        events &= ~POLL_IN;
 | 
			
		||||
        result |= POLLIN;
 | 
			
		||||
    }
 | 
			
		||||
    if (events & POLL_PRI) {
 | 
			
		||||
        events &= ~POLL_PRI;
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
        LOG_WARNING(Service, "Winsock doesn't support POLLPRI");
 | 
			
		||||
#else
 | 
			
		||||
        result |= POLL_PRI;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    if (events & POLL_OUT) {
 | 
			
		||||
        events &= ~POLL_OUT;
 | 
			
		||||
        result |= POLLOUT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    UNIMPLEMENTED_IF_MSG(events != 0, "Unhandled guest events=0x{:x}", events);
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u16 TranslatePollRevents(u16 revents) {
 | 
			
		||||
    u16 result = 0;
 | 
			
		||||
    const auto translate = [&result, &revents](int host, unsigned guest) {
 | 
			
		||||
        if (revents & host) {
 | 
			
		||||
            revents &= ~host;
 | 
			
		||||
            result |= guest;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    translate(POLLIN, POLL_IN);
 | 
			
		||||
    translate(POLLPRI, POLL_PRI);
 | 
			
		||||
    translate(POLLOUT, POLL_OUT);
 | 
			
		||||
    translate(POLLERR, POLL_ERR);
 | 
			
		||||
    translate(POLLHUP, POLL_HUP);
 | 
			
		||||
 | 
			
		||||
    UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents);
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
Errno SetSockOpt(SOCKET fd, int option, T value) {
 | 
			
		||||
    const int result =
 | 
			
		||||
        setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
 | 
			
		||||
    if (result != SOCKET_ERROR) {
 | 
			
		||||
        return Errno::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
    const int ec = LastError();
 | 
			
		||||
    UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
    return Errno::SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // Anonymous namespace
 | 
			
		||||
 | 
			
		||||
NetworkInstance::NetworkInstance() {
 | 
			
		||||
    Initialize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NetworkInstance::~NetworkInstance() {
 | 
			
		||||
    Finalize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<IPv4Address, Errno> GetHostIPv4Address() {
 | 
			
		||||
    std::array<char, 256> name{};
 | 
			
		||||
    if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) {
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unhandled gethostname error");
 | 
			
		||||
        return {IPv4Address{}, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hostent* const ent = gethostbyname(name.data());
 | 
			
		||||
    if (!ent) {
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unhandled gethostbyname error");
 | 
			
		||||
        return {IPv4Address{}, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
    if (ent->h_addr_list == nullptr) {
 | 
			
		||||
        UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list");
 | 
			
		||||
        return {IPv4Address{}, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
    if (ent->h_length != sizeof(in_addr)) {
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    in_addr addr;
 | 
			
		||||
    std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr));
 | 
			
		||||
    return {TranslateIPv4(addr), Errno::SUCCESS};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
 | 
			
		||||
    const size_t num = pollfds.size();
 | 
			
		||||
 | 
			
		||||
    std::vector<WSAPOLLFD> host_pollfds(pollfds.size());
 | 
			
		||||
    std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) {
 | 
			
		||||
        WSAPOLLFD result;
 | 
			
		||||
        result.fd = fd.socket->fd;
 | 
			
		||||
        result.events = TranslatePollEvents(fd.events);
 | 
			
		||||
        result.revents = 0;
 | 
			
		||||
        return result;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout);
 | 
			
		||||
    if (result == 0) {
 | 
			
		||||
        ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(),
 | 
			
		||||
                           [](WSAPOLLFD fd) { return fd.revents == 0; }));
 | 
			
		||||
        return {0, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < num; ++i) {
 | 
			
		||||
        pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (result > 0) {
 | 
			
		||||
        return {result, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ASSERT(result == SOCKET_ERROR);
 | 
			
		||||
 | 
			
		||||
    const int ec = LastError();
 | 
			
		||||
    UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
    return {-1, Errno::SUCCESS};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Socket::~Socket() {
 | 
			
		||||
    if (fd == INVALID_SOCKET) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    (void)closesocket(fd);
 | 
			
		||||
    fd = INVALID_SOCKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {}
 | 
			
		||||
 | 
			
		||||
Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
 | 
			
		||||
    fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol));
 | 
			
		||||
    if (fd != INVALID_SOCKET) {
 | 
			
		||||
        return Errno::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int ec = LastError();
 | 
			
		||||
    UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
    return Errno::SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
 | 
			
		||||
    sockaddr addr;
 | 
			
		||||
    socklen_t addrlen = sizeof(addr);
 | 
			
		||||
    const SOCKET new_socket = accept(fd, &addr, &addrlen);
 | 
			
		||||
 | 
			
		||||
    if (new_socket == INVALID_SOCKET) {
 | 
			
		||||
        const int ec = LastError();
 | 
			
		||||
        UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
        return {AcceptResult{}, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    AcceptResult result;
 | 
			
		||||
    result.socket = std::make_unique<Socket>();
 | 
			
		||||
    result.socket->fd = new_socket;
 | 
			
		||||
 | 
			
		||||
    ASSERT(addrlen == sizeof(sockaddr_in));
 | 
			
		||||
    result.sockaddr_in = TranslateToSockAddrIn(addr);
 | 
			
		||||
 | 
			
		||||
    return {std::move(result), Errno::SUCCESS};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::Connect(SockAddrIn addr_in) {
 | 
			
		||||
    const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in);
 | 
			
		||||
    if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != INVALID_SOCKET) {
 | 
			
		||||
        return Errno::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (const int ec = LastError()) {
 | 
			
		||||
    case WSAEWOULDBLOCK:
 | 
			
		||||
        LOG_DEBUG(Service, "EAGAIN generated");
 | 
			
		||||
        return Errno::AGAIN;
 | 
			
		||||
    default:
 | 
			
		||||
        UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
        return Errno::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<SockAddrIn, Errno> Socket::GetPeerName() {
 | 
			
		||||
    sockaddr addr;
 | 
			
		||||
    socklen_t addrlen = sizeof(addr);
 | 
			
		||||
    if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) {
 | 
			
		||||
        const int ec = LastError();
 | 
			
		||||
        UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
        return {SockAddrIn{}, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ASSERT(addrlen == sizeof(sockaddr_in));
 | 
			
		||||
    return {TranslateToSockAddrIn(addr), Errno::SUCCESS};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<SockAddrIn, Errno> Socket::GetSockName() {
 | 
			
		||||
    sockaddr addr;
 | 
			
		||||
    socklen_t addrlen = sizeof(addr);
 | 
			
		||||
    if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) {
 | 
			
		||||
        const int ec = LastError();
 | 
			
		||||
        UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
        return {SockAddrIn{}, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ASSERT(addrlen == sizeof(sockaddr_in));
 | 
			
		||||
    return {TranslateToSockAddrIn(addr), Errno::SUCCESS};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::Bind(SockAddrIn addr) {
 | 
			
		||||
    const sockaddr addr_in = TranslateFromSockAddrIn(addr);
 | 
			
		||||
    if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) {
 | 
			
		||||
        return Errno::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int ec = LastError();
 | 
			
		||||
    UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
    return Errno::SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::Listen(s32 backlog) {
 | 
			
		||||
    if (listen(fd, backlog) != SOCKET_ERROR) {
 | 
			
		||||
        return Errno::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int ec = LastError();
 | 
			
		||||
    UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
    return Errno::SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::Shutdown(ShutdownHow how) {
 | 
			
		||||
    int host_how = 0;
 | 
			
		||||
    switch (how) {
 | 
			
		||||
    case ShutdownHow::RD:
 | 
			
		||||
        host_how = SD_RECEIVE;
 | 
			
		||||
        break;
 | 
			
		||||
    case ShutdownHow::WR:
 | 
			
		||||
        host_how = SD_SEND;
 | 
			
		||||
        break;
 | 
			
		||||
    case ShutdownHow::RDWR:
 | 
			
		||||
        host_how = SD_BOTH;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        UNIMPLEMENTED_MSG("Unimplemented flag how={}", static_cast<int>(how));
 | 
			
		||||
        return Errno::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
    if (shutdown(fd, host_how) != SOCKET_ERROR) {
 | 
			
		||||
        return Errno::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (const int ec = LastError()) {
 | 
			
		||||
    case WSAENOTCONN:
 | 
			
		||||
        LOG_ERROR(Service, "ENOTCONN generated");
 | 
			
		||||
        return Errno::NOTCONN;
 | 
			
		||||
    default:
 | 
			
		||||
        UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
        return Errno::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
 | 
			
		||||
    ASSERT(flags == 0);
 | 
			
		||||
    ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
 | 
			
		||||
 | 
			
		||||
    const int result =
 | 
			
		||||
        recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0);
 | 
			
		||||
    if (result != SOCKET_ERROR) {
 | 
			
		||||
        return {result, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (const int ec = LastError()) {
 | 
			
		||||
    case WSAEWOULDBLOCK:
 | 
			
		||||
        LOG_DEBUG(Service, "EAGAIN generated");
 | 
			
		||||
        return {-1, Errno::AGAIN};
 | 
			
		||||
    case WSAENOTCONN:
 | 
			
		||||
        LOG_ERROR(Service, "ENOTCONN generated");
 | 
			
		||||
        return {-1, Errno::NOTCONN};
 | 
			
		||||
    default:
 | 
			
		||||
        UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
        return {0, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
 | 
			
		||||
    ASSERT(flags == 0);
 | 
			
		||||
    ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
 | 
			
		||||
 | 
			
		||||
    sockaddr addr_in{};
 | 
			
		||||
    socklen_t addrlen = sizeof(addr_in);
 | 
			
		||||
    socklen_t* const p_addrlen = addr ? &addrlen : nullptr;
 | 
			
		||||
    sockaddr* const p_addr_in = addr ? &addr_in : nullptr;
 | 
			
		||||
 | 
			
		||||
    const int result = recvfrom(fd, reinterpret_cast<char*>(message.data()),
 | 
			
		||||
                                static_cast<int>(message.size()), 0, p_addr_in, p_addrlen);
 | 
			
		||||
    if (result != SOCKET_ERROR) {
 | 
			
		||||
        if (addr) {
 | 
			
		||||
            ASSERT(addrlen == sizeof(addr_in));
 | 
			
		||||
            *addr = TranslateToSockAddrIn(addr_in);
 | 
			
		||||
        }
 | 
			
		||||
        return {result, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (const int ec = LastError()) {
 | 
			
		||||
    case WSAEWOULDBLOCK:
 | 
			
		||||
        LOG_DEBUG(Service, "EAGAIN generated");
 | 
			
		||||
        return {-1, Errno::AGAIN};
 | 
			
		||||
    case WSAENOTCONN:
 | 
			
		||||
        LOG_ERROR(Service, "ENOTCONN generated");
 | 
			
		||||
        return {-1, Errno::NOTCONN};
 | 
			
		||||
    default:
 | 
			
		||||
        UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
        return {-1, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
 | 
			
		||||
    ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
 | 
			
		||||
    ASSERT(flags == 0);
 | 
			
		||||
 | 
			
		||||
    const int result = send(fd, reinterpret_cast<const char*>(message.data()),
 | 
			
		||||
                            static_cast<int>(message.size()), 0);
 | 
			
		||||
    if (result != SOCKET_ERROR) {
 | 
			
		||||
        return {result, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int ec = LastError();
 | 
			
		||||
    switch (ec) {
 | 
			
		||||
    case WSAEWOULDBLOCK:
 | 
			
		||||
        LOG_DEBUG(Service, "EAGAIN generated");
 | 
			
		||||
        return {-1, Errno::AGAIN};
 | 
			
		||||
    case WSAENOTCONN:
 | 
			
		||||
        LOG_ERROR(Service, "ENOTCONN generated");
 | 
			
		||||
        return {-1, Errno::NOTCONN};
 | 
			
		||||
    default:
 | 
			
		||||
        UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
        return {-1, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
 | 
			
		||||
                                     const SockAddrIn* addr) {
 | 
			
		||||
    ASSERT(flags == 0);
 | 
			
		||||
 | 
			
		||||
    const sockaddr* to = nullptr;
 | 
			
		||||
    const int tolen = addr ? 0 : sizeof(sockaddr);
 | 
			
		||||
    sockaddr host_addr_in;
 | 
			
		||||
 | 
			
		||||
    if (addr) {
 | 
			
		||||
        host_addr_in = TranslateFromSockAddrIn(*addr);
 | 
			
		||||
        to = &host_addr_in;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int result = sendto(fd, reinterpret_cast<const char*>(message.data()),
 | 
			
		||||
                              static_cast<int>(message.size()), 0, to, tolen);
 | 
			
		||||
    if (result != SOCKET_ERROR) {
 | 
			
		||||
        return {result, Errno::SUCCESS};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int ec = LastError();
 | 
			
		||||
    UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
    return {-1, Errno::SUCCESS};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::Close() {
 | 
			
		||||
    [[maybe_unused]] const int result = closesocket(fd);
 | 
			
		||||
    ASSERT(result == 0);
 | 
			
		||||
    fd = INVALID_SOCKET;
 | 
			
		||||
 | 
			
		||||
    return Errno::SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::SetLinger(bool enable, u32 linger) {
 | 
			
		||||
    return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::SetReuseAddr(bool enable) {
 | 
			
		||||
    return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::SetBroadcast(bool enable) {
 | 
			
		||||
    return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::SetSndBuf(u32 value) {
 | 
			
		||||
    return SetSockOpt(fd, SO_SNDBUF, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::SetRcvBuf(u32 value) {
 | 
			
		||||
    return SetSockOpt(fd, SO_RCVBUF, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::SetSndTimeo(u32 value) {
 | 
			
		||||
    return SetSockOpt(fd, SO_SNDTIMEO, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::SetRcvTimeo(u32 value) {
 | 
			
		||||
    return SetSockOpt(fd, SO_RCVTIMEO, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Errno Socket::SetNonBlock(bool enable) {
 | 
			
		||||
    if (EnableNonBlock(fd, enable)) {
 | 
			
		||||
        return Errno::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
    const int ec = LastError();
 | 
			
		||||
    UNREACHABLE_MSG("Unhandled host socket error={}", ec);
 | 
			
		||||
    return Errno::SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Socket::IsOpened() const {
 | 
			
		||||
    return fd != INVALID_SOCKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Network
 | 
			
		||||
							
								
								
									
										87
									
								
								src/core/network/network.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/core/network/network.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
// Copyright 2020 yuzu emulator team
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace Network {
 | 
			
		||||
 | 
			
		||||
class Socket;
 | 
			
		||||
 | 
			
		||||
/// Error code for network functions
 | 
			
		||||
enum class Errno {
 | 
			
		||||
    SUCCESS,
 | 
			
		||||
    BADF,
 | 
			
		||||
    INVAL,
 | 
			
		||||
    MFILE,
 | 
			
		||||
    NOTCONN,
 | 
			
		||||
    AGAIN,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Address families
 | 
			
		||||
enum class Domain {
 | 
			
		||||
    INET, ///< Address family for IPv4
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Socket types
 | 
			
		||||
enum class Type {
 | 
			
		||||
    STREAM,
 | 
			
		||||
    DGRAM,
 | 
			
		||||
    RAW,
 | 
			
		||||
    SEQPACKET,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Protocol values for sockets
 | 
			
		||||
enum class Protocol {
 | 
			
		||||
    ICMP,
 | 
			
		||||
    TCP,
 | 
			
		||||
    UDP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Shutdown mode
 | 
			
		||||
enum class ShutdownHow {
 | 
			
		||||
    RD,
 | 
			
		||||
    WR,
 | 
			
		||||
    RDWR,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Array of IPv4 address
 | 
			
		||||
using IPv4Address = std::array<u8, 4>;
 | 
			
		||||
 | 
			
		||||
/// Cross-platform sockaddr structure
 | 
			
		||||
struct SockAddrIn {
 | 
			
		||||
    Domain family;
 | 
			
		||||
    IPv4Address ip;
 | 
			
		||||
    u16 portno;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Cross-platform poll fd structure
 | 
			
		||||
struct PollFD {
 | 
			
		||||
    Socket* socket;
 | 
			
		||||
    u16 events;
 | 
			
		||||
    u16 revents;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
constexpr u16 POLL_IN = 1 << 0;
 | 
			
		||||
constexpr u16 POLL_PRI = 1 << 1;
 | 
			
		||||
constexpr u16 POLL_OUT = 1 << 2;
 | 
			
		||||
constexpr u16 POLL_ERR = 1 << 3;
 | 
			
		||||
constexpr u16 POLL_HUP = 1 << 4;
 | 
			
		||||
constexpr u16 POLL_NVAL = 1 << 5;
 | 
			
		||||
 | 
			
		||||
class NetworkInstance {
 | 
			
		||||
public:
 | 
			
		||||
    explicit NetworkInstance();
 | 
			
		||||
    ~NetworkInstance();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// @brief Returns host's IPv4 address
 | 
			
		||||
/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code
 | 
			
		||||
std::pair<IPv4Address, Errno> GetHostIPv4Address();
 | 
			
		||||
 | 
			
		||||
} // namespace Network
 | 
			
		||||
							
								
								
									
										94
									
								
								src/core/network/sockets.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/core/network/sockets.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
// Copyright 2020 yuzu emulator team
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
#include <winsock.h>
 | 
			
		||||
#elif !defined(__unix__)
 | 
			
		||||
#error "Platform not implemented"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "core/network/network.h"
 | 
			
		||||
 | 
			
		||||
// TODO: C++20 Replace std::vector usages with std::span
 | 
			
		||||
 | 
			
		||||
namespace Network {
 | 
			
		||||
 | 
			
		||||
class Socket {
 | 
			
		||||
public:
 | 
			
		||||
    struct AcceptResult {
 | 
			
		||||
        std::unique_ptr<Socket> socket;
 | 
			
		||||
        SockAddrIn sockaddr_in;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    explicit Socket() = default;
 | 
			
		||||
    ~Socket();
 | 
			
		||||
 | 
			
		||||
    Socket(const Socket&) = delete;
 | 
			
		||||
    Socket& operator=(const Socket&) = delete;
 | 
			
		||||
 | 
			
		||||
    Socket(Socket&& rhs) noexcept;
 | 
			
		||||
 | 
			
		||||
    // Avoid closing sockets implicitly
 | 
			
		||||
    Socket& operator=(Socket&&) noexcept = delete;
 | 
			
		||||
 | 
			
		||||
    Errno Initialize(Domain domain, Type type, Protocol protocol);
 | 
			
		||||
 | 
			
		||||
    Errno Close();
 | 
			
		||||
 | 
			
		||||
    std::pair<AcceptResult, Errno> Accept();
 | 
			
		||||
 | 
			
		||||
    Errno Connect(SockAddrIn addr_in);
 | 
			
		||||
 | 
			
		||||
    std::pair<SockAddrIn, Errno> GetPeerName();
 | 
			
		||||
 | 
			
		||||
    std::pair<SockAddrIn, Errno> GetSockName();
 | 
			
		||||
 | 
			
		||||
    Errno Bind(SockAddrIn addr);
 | 
			
		||||
 | 
			
		||||
    Errno Listen(s32 backlog);
 | 
			
		||||
 | 
			
		||||
    Errno Shutdown(ShutdownHow how);
 | 
			
		||||
 | 
			
		||||
    std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message);
 | 
			
		||||
 | 
			
		||||
    std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr);
 | 
			
		||||
 | 
			
		||||
    std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags);
 | 
			
		||||
 | 
			
		||||
    std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr);
 | 
			
		||||
 | 
			
		||||
    Errno SetLinger(bool enable, u32 linger);
 | 
			
		||||
 | 
			
		||||
    Errno SetReuseAddr(bool enable);
 | 
			
		||||
 | 
			
		||||
    Errno SetBroadcast(bool enable);
 | 
			
		||||
 | 
			
		||||
    Errno SetSndBuf(u32 value);
 | 
			
		||||
 | 
			
		||||
    Errno SetRcvBuf(u32 value);
 | 
			
		||||
 | 
			
		||||
    Errno SetSndTimeo(u32 value);
 | 
			
		||||
 | 
			
		||||
    Errno SetRcvTimeo(u32 value);
 | 
			
		||||
 | 
			
		||||
    Errno SetNonBlock(bool enable);
 | 
			
		||||
 | 
			
		||||
    bool IsOpened() const;
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    SOCKET fd = INVALID_SOCKET;
 | 
			
		||||
#elif defined(__unix__)
 | 
			
		||||
    int fd = -1;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
 | 
			
		||||
 | 
			
		||||
} // namespace Network
 | 
			
		||||
		Reference in New Issue
	
	Block a user