core, network: Add ability to proxy socket packets

This commit is contained in:
FearlessTobi 2022-07-30 05:58:23 +02:00
parent 035ca99b02
commit f80c7c4cd5
28 changed files with 1039 additions and 537 deletions

View file

@ -6,7 +6,6 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/service.h"
namespace {
@ -271,142 +270,45 @@ public:
}
};
class IGeneralService final : public ServiceFramework<IGeneralService> {
public:
explicit IGeneralService(Core::System& system_);
void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) {
static constexpr u32 client_id = 1;
LOG_WARNING(Service_NIFM, "(STUBBED) called");
private:
void GetClientId(Kernel::HLERequestContext& ctx) {
static constexpr u32 client_id = 1;
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
}
void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
void CreateScanRequest(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IScanRequest>(system);
}
rb.Push(ResultSuccess);
rb.PushIpcInterface<IScanRequest>(system);
}
void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
void CreateRequest(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IRequest>(system);
}
rb.Push(ResultSuccess);
rb.PushIpcInterface<IRequest>(system);
}
void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
const auto net_iface = Network::GetSelectedNetworkInterface();
const auto net_iface = Network::GetSelectedNetworkInterface();
const SfNetworkProfileData network_profile_data = [&net_iface] {
if (!net_iface) {
return SfNetworkProfileData{};
}
return SfNetworkProfileData{
.ip_setting_data{
.ip_address_setting{
.is_automatic{true},
.current_address{Network::TranslateIPv4(net_iface->ip_address)},
.subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
.gateway{Network::TranslateIPv4(net_iface->gateway)},
},
.dns_setting{
.is_automatic{true},
.primary_dns{1, 1, 1, 1},
.secondary_dns{1, 0, 0, 1},
},
.proxy_setting{
.enabled{false},
.port{},
.proxy_server{},
.automatic_auth_enabled{},
.user{},
.password{},
},
.mtu{1500},
},
.uuid{0xdeadbeef, 0xdeadbeef},
.network_name{"yuzu Network"},
.wireless_setting_data{
.ssid_length{12},
.ssid{"yuzu Network"},
.passphrase{"yuzupassword"},
},
};
}();
ctx.WriteBuffer(network_profile_data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
auto ipv4 = Network::GetHostIPv4Address();
if (!ipv4) {
LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
SfNetworkProfileData network_profile_data = [&net_iface] {
if (!net_iface) {
return SfNetworkProfileData{};
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(*ipv4);
}
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c,
"SfNetworkProfileData is not the correct size");
u128 uuid{};
auto buffer = ctx.ReadBuffer();
std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<INetworkProfile>(system);
rb.PushRaw<u128>(uuid);
}
void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
struct IpConfigInfo {
IpAddressSetting ip_address_setting{};
DnsSetting dns_setting{};
};
static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
"IpConfigInfo has incorrect size.");
const auto net_iface = Network::GetSelectedNetworkInterface();
const IpConfigInfo ip_config_info = [&net_iface] {
if (!net_iface) {
return IpConfigInfo{};
}
return IpConfigInfo{
return SfNetworkProfileData{
.ip_setting_data{
.ip_address_setting{
.is_automatic{true},
.current_address{Network::TranslateIPv4(net_iface->ip_address)},
@ -418,66 +320,177 @@ private:
.primary_dns{1, 1, 1, 1},
.secondary_dns{1, 0, 0, 1},
},
};
}();
.proxy_setting{
.enabled{false},
.port{},
.proxy_server{},
.automatic_auth_enabled{},
.user{},
.password{},
},
.mtu{1500},
},
.uuid{0xdeadbeef, 0xdeadbeef},
.network_name{"yuzu Network"},
.wireless_setting_data{
.ssid_length{12},
.ssid{"yuzu Network"},
.passphrase{"yuzupassword"},
},
};
}();
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<IpConfigInfo>(ip_config_info);
// When we're connected to a room, spoof the hosts IP address
if (auto room_member = network.GetRoomMember().lock()) {
if (room_member->IsConnected()) {
network_profile_data.ip_setting_data.ip_address_setting.current_address =
room_member->GetFakeIpAddress();
}
}
void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
ctx.WriteBuffer(network_profile_data);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
auto ipv4 = Network::GetHostIPv4Address();
if (!ipv4) {
LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
}
// When we're connected to a room, spoof the hosts IP address
if (auto room_member = network.GetRoomMember().lock()) {
if (room_member->IsConnected()) {
ipv4 = room_member->GetFakeIpAddress();
}
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(*ipv4);
}
void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size");
u128 uuid{};
auto buffer = ctx.ReadBuffer();
std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<INetworkProfile>(system);
rb.PushRaw<u128>(uuid);
}
void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
struct IpConfigInfo {
IpAddressSetting ip_address_setting{};
DnsSetting dns_setting{};
};
static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
"IpConfigInfo has incorrect size.");
const auto net_iface = Network::GetSelectedNetworkInterface();
IpConfigInfo ip_config_info = [&net_iface] {
if (!net_iface) {
return IpConfigInfo{};
}
return IpConfigInfo{
.ip_address_setting{
.is_automatic{true},
.current_address{Network::TranslateIPv4(net_iface->ip_address)},
.subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
.gateway{Network::TranslateIPv4(net_iface->gateway)},
},
.dns_setting{
.is_automatic{true},
.primary_dns{1, 1, 1, 1},
.secondary_dns{1, 0, 0, 1},
},
};
}();
// When we're connected to a room, spoof the hosts IP address
if (auto room_member = network.GetRoomMember().lock()) {
if (room_member->IsConnected()) {
ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress();
}
}
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<IpConfigInfo>(ip_config_info);
}
void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(1);
}
void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
struct Output {
InternetConnectionType type{InternetConnectionType::WiFi};
u8 wifi_strength{3};
InternetConnectionStatus state{InternetConnectionStatus::Connected};
};
static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
constexpr Output out{};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(out);
}
void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
if (Network::GetHostIPv4Address().has_value()) {
rb.Push<u8>(1);
} else {
rb.Push<u8>(0);
}
}
void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_NIFM, "(STUBBED) called");
struct Output {
InternetConnectionType type{InternetConnectionType::WiFi};
u8 wifi_strength{3};
InternetConnectionStatus state{InternetConnectionStatus::Connected};
};
static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
constexpr Output out{};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(out);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
if (Network::GetHostIPv4Address().has_value()) {
rb.Push<u8>(1);
} else {
rb.Push<u8>(0);
}
void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
if (Network::GetHostIPv4Address().has_value()) {
rb.Push<u8>(1);
} else {
rb.Push<u8>(0);
}
}
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
if (Network::GetHostIPv4Address().has_value()) {
rb.Push<u8>(1);
} else {
rb.Push<u8>(0);
}
}
};
}
IGeneralService::IGeneralService(Core::System& system_)
: ServiceFramework{system_, "IGeneralService"} {
: ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{1, &IGeneralService::GetClientId, "GetClientId"},
@ -528,6 +541,8 @@ IGeneralService::IGeneralService(Core::System& system_)
RegisterHandlers(functions);
}
IGeneralService::~IGeneralService() = default;
class NetworkInterface final : public ServiceFramework<NetworkInterface> {
public:
explicit NetworkInterface(const char* name, Core::System& system_)

View file

@ -3,6 +3,11 @@
#pragma once
#include "core/hle/service/service.h"
#include "network/network.h"
#include "network/room.h"
#include "network/room_member.h"
namespace Core {
class System;
}
@ -16,4 +21,26 @@ namespace Service::NIFM {
/// Registers all NIFM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
class IGeneralService final : public ServiceFramework<IGeneralService> {
public:
explicit IGeneralService(Core::System& system_);
~IGeneralService() override;
private:
void GetClientId(Kernel::HLERequestContext& ctx);
void CreateScanRequest(Kernel::HLERequestContext& ctx);
void CreateRequest(Kernel::HLERequestContext& ctx);
void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx);
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx);
void GetCurrentIpAddress(Kernel::HLERequestContext& ctx);
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx);
void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx);
void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx);
void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx);
void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx);
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx);
Network::RoomNetwork& network;
};
} // namespace Service::NIFM

View file

@ -9,12 +9,16 @@
#include <fmt/format.h>
#include "common/microprofile.h"
#include "common/socket_types.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_thread.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"
namespace Service::Sockets {
@ -472,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
LOG_INFO(Service, "New socket fd={}", fd);
descriptor.socket = std::make_unique<Network::Socket>();
auto room_member = room_network.GetRoomMember().lock();
if (room_member && room_member->IsConnected()) {
descriptor.socket = std::make_unique<Network::ProxySocket>(room_network);
} else {
descriptor.socket = std::make_unique<Network::Socket>();
}
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol));
descriptor.is_connection_based = IsConnectionBased(type);
@ -648,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
ASSERT(arg == 0);
return {descriptor.flags, Errno::SUCCESS};
case FcntlCmd::SETFL: {
const bool enable = (arg & FLAG_O_NONBLOCK) != 0;
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};
@ -669,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
return Errno::BADF;
}
Network::Socket* const socket = file_descriptors[fd]->socket.get();
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
if (optname == OptName::LINGER) {
ASSERT(optlen == sizeof(Linger));
@ -724,6 +734,8 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message)
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) {
@ -759,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
}
// 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) {
@ -857,8 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
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, ServiceThreadType::CreateNew} {
: ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{
system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BSD::RegisterClient, "RegisterClient"},
@ -899,6 +924,13 @@ BSD::BSD(Core::System& system_, const char* name)
// 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 initalized");
}
}
BSD::~BSD() = default;

View file

@ -7,14 +7,17 @@
#include <vector>
#include "common/common_types.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
@ -30,7 +33,7 @@ private:
static constexpr size_t MAX_FD = 128;
struct FileDescriptor {
std::unique_ptr<Network::Socket> socket;
std::unique_ptr<Network::SocketBase> socket;
s32 flags = 0;
bool is_connection_based = false;
};
@ -165,6 +168,14 @@ private:
void BuildErrnoResponse(Kernel::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;
};
class BSDCFG final : public ServiceFramework<BSDCFG> {

View file

@ -22,7 +22,9 @@ enum class Errno : u32 {
AGAIN = 11,
INVAL = 22,
MFILE = 24,
MSGSIZE = 90,
NOTCONN = 107,
TIMEDOUT = 110,
};
enum class Domain : u32 {
@ -96,10 +98,6 @@ struct Linger {
u32 linger;
};
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
constexpr u32 FLAG_O_NONBLOCK = 0x800;
/// Registers all Sockets services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);

View file

@ -25,6 +25,8 @@ Errno Translate(Network::Errno value) {
return Errno::MFILE;
case Network::Errno::NOTCONN:
return Errno::NOTCONN;
case Network::Errno::TIMEDOUT:
return Errno::TIMEDOUT;
default:
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
return Errno::SUCCESS;