mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-11 22:32:29 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a22f1845b | |||
| 301da63a15 | |||
| c95ad020fb |
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -9,7 +12,9 @@
|
|||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
Manager::Manager(Core::System& system_)
|
Manager::Manager(Core::System& system_)
|
||||||
: system{system_}, system_manager{std::make_unique<SystemManager>(system)} {
|
: system{system_}
|
||||||
|
, system_manager{std::make_unique<SystemManager>(system)}
|
||||||
|
{
|
||||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,14 +43,12 @@ Result Manager::GetWorkBufferSize(const AudioRendererParameterInternal& params,
|
|||||||
|
|
||||||
s32 Manager::GetSessionId() {
|
s32 Manager::GetSessionId() {
|
||||||
std::scoped_lock l{session_lock};
|
std::scoped_lock l{session_lock};
|
||||||
auto session_id{session_ids[session_count]};
|
ASSERT(session_count <= session_ids.size());
|
||||||
|
auto const session_id = session_ids[session_count];
|
||||||
if (session_id == -1) {
|
if (session_id >= 0) {
|
||||||
return -1;
|
session_ids[session_count] = -1;
|
||||||
|
session_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
session_ids[session_count] = -1;
|
|
||||||
session_count++;
|
|
||||||
return session_id;
|
return session_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,10 +28,18 @@ namespace {
|
|||||||
//
|
//
|
||||||
// Keep in sync with cubeb_sink.cpp name.
|
// Keep in sync with cubeb_sink.cpp name.
|
||||||
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
|
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
SDL_SetHintWithPriority(SDL_HINT_AUDIO_DRIVER, "openslES", SDL_HINT_OVERRIDE);
|
||||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||||
|
LOG_WARNING(Audio_Sink, "OpenSL ES audio initialization failed: {}; retrying default drivers", SDL_GetError());
|
||||||
|
SDL_ResetHint(SDL_HINT_AUDIO_DRIVER);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!SDL_WasInit(SDL_INIT_AUDIO) && !SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
LOG_INFO(Audio_Sink, "SDL audio driver: {}", SDL_GetCurrentAudioDriver());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-6
@@ -1261,11 +1261,23 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 OR ARCHITECTURE_riscv64 OR ARCHITE
|
|||||||
target_link_libraries(core PRIVATE dynarmic::dynarmic)
|
target_link_libraries(core PRIVATE dynarmic::dynarmic)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (TARGET OpenSSL::SSL)
|
target_sources(core PRIVATE hle/service/ssl/ssl_backend_openssl.cpp)
|
||||||
target_sources(core PRIVATE hle/service/ssl/ssl_backend_openssl.cpp)
|
|
||||||
target_link_libraries(core PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
target_link_libraries(core PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||||
else()
|
|
||||||
target_sources(core PRIVATE hle/service/ssl/ssl_backend_none.cpp)
|
# TODO
|
||||||
endif()
|
|
||||||
|
# elseif (APPLE)
|
||||||
|
# target_sources(core PRIVATE
|
||||||
|
# hle/service/ssl/ssl_backend_securetransport.cpp)
|
||||||
|
# target_link_libraries(core PRIVATE "-framework Security")
|
||||||
|
# elseif (WIN32)
|
||||||
|
# target_sources(core PRIVATE
|
||||||
|
# hle/service/ssl/ssl_backend_schannel.cpp)
|
||||||
|
# target_link_libraries(core PRIVATE crypt32 secur32)
|
||||||
|
# else()
|
||||||
|
# target_sources(core PRIVATE
|
||||||
|
# hle/service/ssl/ssl_backend_none.cpp)
|
||||||
|
# endif()
|
||||||
|
|
||||||
create_target_directory_groups(core)
|
create_target_directory_groups(core)
|
||||||
|
|||||||
@@ -185,26 +185,26 @@ public:
|
|||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("aud:a", std::make_shared<IAudioSystemManagerForApplet>(system));
|
server_manager->RegisterNamedService("aud:a", std::make_shared<IAudioSystemManagerForApplet>(system), 30);
|
||||||
server_manager->RegisterNamedService("aud:d", std::make_shared<IAudioSystemManagerForDebugger>(system));
|
server_manager->RegisterNamedService("aud:d", std::make_shared<IAudioSystemManagerForDebugger>(system), 30);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system));
|
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system));
|
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system));
|
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system));
|
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system), 30);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system));
|
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
|
||||||
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system));
|
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
|
||||||
server_manager->RegisterNamedService("audout:u", std::make_shared<IAudioOutManager>(system));
|
server_manager->RegisterNamedService("audout:u", std::make_shared<IAudioOutManager>(system), 30);
|
||||||
server_manager->RegisterNamedService("audout:a", std::make_shared<IAudioOutManagerForApplet>(system));
|
server_manager->RegisterNamedService("audout:a", std::make_shared<IAudioOutManagerForApplet>(system), 30);
|
||||||
server_manager->RegisterNamedService("auddev", std::make_shared<IAudioSnoopManager>(system));
|
server_manager->RegisterNamedService("auddev", std::make_shared<IAudioSnoopManager>(system), 30);
|
||||||
// Depends on audout:u and audin:u on ctor!
|
// Depends on audout:u and audin:u on ctor!
|
||||||
server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(system));
|
server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(system), 30);
|
||||||
server_manager->RegisterNamedService("audrec:a", std::make_shared<IFinalOutputRecorderManagerForApplet>(system));
|
server_manager->RegisterNamedService("audrec:a", std::make_shared<IFinalOutputRecorderManagerForApplet>(system), 30);
|
||||||
server_manager->RegisterNamedService("audrec:u", std::make_shared<IFinalOutputRecorderManager>(system));
|
server_manager->RegisterNamedService("audrec:u", std::make_shared<IFinalOutputRecorderManager>(system), 30);
|
||||||
server_manager->RegisterNamedService("audren:u", std::make_shared<IAudioRendererManager>(system));
|
server_manager->RegisterNamedService("audren:u", std::make_shared<IAudioRendererManager>(system), 30);
|
||||||
server_manager->RegisterNamedService("audren:a", std::make_shared<IAudioRendererManagerForApplet>(system));
|
server_manager->RegisterNamedService("audren:a", std::make_shared<IAudioRendererManagerForApplet>(system), 30);
|
||||||
server_manager->RegisterNamedService("hwopus", std::make_shared<IHardwareOpusDecoderManager>(system));
|
server_manager->RegisterNamedService("hwopus", std::make_shared<IHardwareOpusDecoderManager>(system), 25);
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -12,25 +15,16 @@ namespace Service::BCAT {
|
|||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("bcat:a",
|
server_manager->RegisterNamedService("bcat:a", std::make_shared<IServiceCreator>(system, "bcat:a"), 32);
|
||||||
std::make_shared<IServiceCreator>(system, "bcat:a"));
|
server_manager->RegisterNamedService("bcat:m", std::make_shared<IServiceCreator>(system, "bcat:m"), 32);
|
||||||
server_manager->RegisterNamedService("bcat:m",
|
server_manager->RegisterNamedService("bcat:u", std::make_shared<IServiceCreator>(system, "bcat:u"), 32);
|
||||||
std::make_shared<IServiceCreator>(system, "bcat:m"));
|
server_manager->RegisterNamedService("bcat:s", std::make_shared<IServiceCreator>(system, "bcat:s"), 32);
|
||||||
server_manager->RegisterNamedService("bcat:u",
|
|
||||||
std::make_shared<IServiceCreator>(system, "bcat:u"));
|
|
||||||
server_manager->RegisterNamedService("bcat:s",
|
|
||||||
std::make_shared<IServiceCreator>(system, "bcat:s"));
|
|
||||||
|
|
||||||
server_manager->RegisterNamedService(
|
server_manager->RegisterNamedService("news:a", std::make_shared<News::IServiceCreator>(system, 0xffffffff, "news:a"), 32);
|
||||||
"news:a", std::make_shared<News::IServiceCreator>(system, 0xffffffff, "news:a"));
|
server_manager->RegisterNamedService("news:p", std::make_shared<News::IServiceCreator>(system, 0x1, "news:p"), 32);
|
||||||
server_manager->RegisterNamedService(
|
server_manager->RegisterNamedService("news:c", std::make_shared<News::IServiceCreator>(system, 0x2, "news:c"), 32);
|
||||||
"news:p", std::make_shared<News::IServiceCreator>(system, 0x1, "news:p"));
|
server_manager->RegisterNamedService("news:v", std::make_shared<News::IServiceCreator>(system, 0x4, "news:v"), 32);
|
||||||
server_manager->RegisterNamedService(
|
server_manager->RegisterNamedService("news:m", std::make_shared<News::IServiceCreator>(system, 0xd, "news:m"), 32);
|
||||||
"news:c", std::make_shared<News::IServiceCreator>(system, 0x2, "news:c"));
|
|
||||||
server_manager->RegisterNamedService(
|
|
||||||
"news:v", std::make_shared<News::IServiceCreator>(system, 0x4, "news:v"));
|
|
||||||
server_manager->RegisterNamedService(
|
|
||||||
"news:m", std::make_shared<News::IServiceCreator>(system, 0xd, "news:m"));
|
|
||||||
|
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,14 +101,28 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BPC_AMS final : public ServiceFramework<BPC_AMS> {
|
||||||
|
public:
|
||||||
|
explicit BPC_AMS(Core::System& system_) : ServiceFramework{system_, "bpc:ams"} {
|
||||||
|
// clang-format off
|
||||||
|
static const FunctionInfo functions[] = {
|
||||||
|
{65000, nullptr, "RebootToFatalError"},
|
||||||
|
{65001, nullptr, "SetRebootPayload"},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
RegisterHandlers(functions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("bpc", std::make_shared<BPC>(system));
|
server_manager->RegisterNamedService("bpc", std::make_shared<BPC>(system), 13);
|
||||||
server_manager->RegisterNamedService("bpc:r", std::make_shared<BPC_R>(system));
|
server_manager->RegisterNamedService("bpc:r", std::make_shared<BPC_R>(system), 13);
|
||||||
server_manager->RegisterNamedService("bpc:c", std::make_shared<BPC_C>(system));
|
server_manager->RegisterNamedService("bpc:c", std::make_shared<BPC_C>(system), 13);
|
||||||
server_manager->RegisterNamedService("bpc:b", std::make_shared<BPC_B>(system));
|
server_manager->RegisterNamedService("bpc:b", std::make_shared<BPC_B>(system), 13);
|
||||||
server_manager->RegisterNamedService("bpc:w", std::make_shared<BPC_W>(system));
|
server_manager->RegisterNamedService("bpc:w", std::make_shared<BPC_W>(system), 13);
|
||||||
|
server_manager->RegisterNamedService("bpc:ams", std::make_shared<BPC_AMS>(system), 4);
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -804,9 +804,9 @@ void LoopProcess(Core::System& system) {
|
|||||||
|
|
||||||
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
|
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
|
||||||
|
|
||||||
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
|
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system), 61);
|
||||||
server_manager->RegisterNamedService("fsp-pr", std::make_shared<FSP_PR>(system));
|
server_manager->RegisterNamedService("fsp-pr", std::make_shared<FSP_PR>(system), 61);
|
||||||
server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
|
server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory), 61);
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,18 +36,18 @@ std::optional<u64> GetTitleIDForProcessID(Core::System& system, u64 process_id)
|
|||||||
ARP_R::ARP_R(Core::System& system_, const ARPManager& manager_)
|
ARP_R::ARP_R(Core::System& system_, const ARPManager& manager_)
|
||||||
: ServiceFramework{system_, "arp:r"}, manager{manager_} {
|
: ServiceFramework{system_, "arp:r"}, manager{manager_} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &ARP_R::GetApplicationLaunchProperty, "GetApplicationLaunchProperty"},
|
{0, &ARP_R::GetApplicationLaunchProperty, "GetApplicationLaunchProperty"},
|
||||||
{1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"},
|
{1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"},
|
||||||
{2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"},
|
{2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"},
|
||||||
{3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"},
|
{3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"},
|
||||||
{4, nullptr, "GetApplicationInstanceUnregistrationNotifier"},
|
{4, nullptr, "GetApplicationInstanceUnregistrationNotifier"},
|
||||||
{5, nullptr, "ListApplicationInstanceId"},
|
{5, nullptr, "ListApplicationInstanceId"},
|
||||||
{6, nullptr, "GetMicroApplicationInstanceId"},
|
{6, nullptr, "GetMicroApplicationInstanceId"},
|
||||||
{7, nullptr, "GetApplicationCertificate"},
|
{7, nullptr, "GetApplicationCertificate"},
|
||||||
{9998, nullptr, "GetPreomiaApplicationLaunchProperty"},
|
{9998, nullptr, "GetPreomiaApplicationLaunchProperty"},
|
||||||
{9999, nullptr, "GetPreomiaApplicationControlProperty"},
|
{9999, nullptr, "GetPreomiaApplicationControlProperty"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
@@ -191,8 +191,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (issued) {
|
if (issued) {
|
||||||
LOG_ERROR(Service_ARP,
|
LOG_ERROR(Service_ARP, "Attempted to issue registrar, but registrar is already issued!");
|
||||||
"Attempted to issue registrar, but registrar is already issued!");
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(Glue::ResultAlreadyBound);
|
rb.Push(Glue::ResultAlreadyBound);
|
||||||
return;
|
return;
|
||||||
@@ -209,9 +208,7 @@ private:
|
|||||||
LOG_DEBUG(Service_ARP, "called");
|
LOG_DEBUG(Service_ARP, "called");
|
||||||
|
|
||||||
if (issued) {
|
if (issued) {
|
||||||
LOG_ERROR(
|
LOG_ERROR(Service_ARP, "Attempted to set application launch property, but registrar is already issued!");
|
||||||
Service_ARP,
|
|
||||||
"Attempted to set application launch property, but registrar is already issued!");
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(Glue::ResultAlreadyBound);
|
rb.Push(Glue::ResultAlreadyBound);
|
||||||
return;
|
return;
|
||||||
@@ -228,9 +225,7 @@ private:
|
|||||||
LOG_DEBUG(Service_ARP, "called");
|
LOG_DEBUG(Service_ARP, "called");
|
||||||
|
|
||||||
if (issued) {
|
if (issued) {
|
||||||
LOG_ERROR(
|
LOG_ERROR(Service_ARP, "Attempted to set application control property, but registrar is already issued!");
|
||||||
Service_ARP,
|
|
||||||
"Attempted to set application control property, but registrar is already issued!");
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(Glue::ResultAlreadyBound);
|
rb.Push(Glue::ResultAlreadyBound);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ void LoopProcess(Core::System& system) {
|
|||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
// ARP
|
// ARP
|
||||||
server_manager->RegisterNamedService("arp:r", std::make_shared<ARP_R>(system, system.GetARPManager()));
|
server_manager->RegisterNamedService("arp:r", std::make_shared<ARP_R>(system, system.GetARPManager()), 16);
|
||||||
server_manager->RegisterNamedService("arp:w", std::make_shared<ARP_W>(system, system.GetARPManager()));
|
server_manager->RegisterNamedService("arp:w", std::make_shared<ARP_W>(system, system.GetARPManager()), 8);
|
||||||
|
|
||||||
// BackGround Task Controller
|
// BackGround Task Controller
|
||||||
server_manager->RegisterNamedService("bgtc:t", std::make_shared<BGTC_T>(system));
|
server_manager->RegisterNamedService("bgtc:t", std::make_shared<BGTC_T>(system));
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ public:
|
|||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("grc:c", std::make_shared<GRC>(system));
|
server_manager->RegisterNamedService("grc:c", std::make_shared<GRC>(system), 4);
|
||||||
server_manager->RegisterNamedService("grc:d", std::make_shared<GRC_D>(system));
|
server_manager->RegisterNamedService("grc:d", std::make_shared<GRC_D>(system), 4);
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -56,9 +59,9 @@ public:
|
|||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("ldr:dmnt", std::make_shared<DebugMonitor>(system));
|
server_manager->RegisterNamedService("ldr:dmnt", std::make_shared<DebugMonitor>(system), 3);
|
||||||
server_manager->RegisterNamedService("ldr:pm", std::make_shared<ProcessManager>(system));
|
server_manager->RegisterNamedService("ldr:pm", std::make_shared<ProcessManager>(system), 1);
|
||||||
server_manager->RegisterNamedService("ldr:shel", std::make_shared<Shell>(system));
|
server_manager->RegisterNamedService("ldr:shel", std::make_shared<Shell>(system), 3);
|
||||||
|
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,9 +169,9 @@ public:
|
|||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system));
|
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4);
|
||||||
server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system));
|
server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4);
|
||||||
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system));
|
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system), 4);
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1144,12 +1144,9 @@ private:
|
|||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("nifm:a",
|
server_manager->RegisterNamedService("nifm:a", std::make_shared<NetworkInterface>("nifm:a", system), 2);
|
||||||
std::make_shared<NetworkInterface>("nifm:a", system));
|
server_manager->RegisterNamedService("nifm:s", std::make_shared<NetworkInterface>("nifm:s", system), 16);
|
||||||
server_manager->RegisterNamedService("nifm:s",
|
server_manager->RegisterNamedService("nifm:u", std::make_shared<NetworkInterface>("nifm:u", system), 5);
|
||||||
std::make_shared<NetworkInterface>("nifm:s", system));
|
|
||||||
server_manager->RegisterNamedService("nifm:u",
|
|
||||||
std::make_shared<NetworkInterface>("nifm:u", system));
|
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,16 +81,16 @@ public:
|
|||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("ns:am2", std::make_shared<IServiceGetterInterface>(system, "ns:am2"));
|
server_manager->RegisterNamedService("ns:am2", std::make_shared<IServiceGetterInterface>(system, "ns:am2"), 5);
|
||||||
server_manager->RegisterNamedService("ns:ec", std::make_shared<IServiceGetterInterface>(system, "ns:ec"));
|
server_manager->RegisterNamedService("ns:ec", std::make_shared<IServiceGetterInterface>(system, "ns:ec"), 5);
|
||||||
server_manager->RegisterNamedService("ns:rid", std::make_shared<IServiceGetterInterface>(system, "ns:rid"));
|
server_manager->RegisterNamedService("ns:rid", std::make_shared<IServiceGetterInterface>(system, "ns:rid"), 5);
|
||||||
server_manager->RegisterNamedService("ns:rt", std::make_shared<IServiceGetterInterface>(system, "ns:rt"));
|
server_manager->RegisterNamedService("ns:rt", std::make_shared<IServiceGetterInterface>(system, "ns:rt"), 5);
|
||||||
server_manager->RegisterNamedService("ns:web", std::make_shared<IServiceGetterInterface>(system, "ns:web"));
|
server_manager->RegisterNamedService("ns:web", std::make_shared<IServiceGetterInterface>(system, "ns:web"), 5);
|
||||||
server_manager->RegisterNamedService("ns:ro", std::make_shared<IServiceGetterInterface>(system, "ns:ro"));
|
server_manager->RegisterNamedService("ns:ro", std::make_shared<IServiceGetterInterface>(system, "ns:ro"), 5);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("ns:dev", std::make_shared<IDevelopInterface>(system));
|
server_manager->RegisterNamedService("ns:dev", std::make_shared<IDevelopInterface>(system), 5);
|
||||||
server_manager->RegisterNamedService("ns:su", std::make_shared<ISystemUpdateInterface>(system));
|
server_manager->RegisterNamedService("ns:su", std::make_shared<ISystemUpdateInterface>(system), 5);
|
||||||
server_manager->RegisterNamedService("ns:vm", std::make_shared<IVulnerabilityManagerInterface>(system));
|
server_manager->RegisterNamedService("ns:vm", std::make_shared<IVulnerabilityManagerInterface>(system), 5);
|
||||||
server_manager->RegisterNamedService("pdm:ntfy", std::make_shared<INotifyService>(system));
|
server_manager->RegisterNamedService("pdm:ntfy", std::make_shared<INotifyService>(system));
|
||||||
server_manager->RegisterNamedService("pdm:qry", std::make_shared<IQueryService>(system));
|
server_manager->RegisterNamedService("pdm:qry", std::make_shared<IQueryService>(system));
|
||||||
|
|
||||||
|
|||||||
@@ -252,10 +252,10 @@ private:
|
|||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("pm:bm", std::make_shared<BootMode>(system));
|
server_manager->RegisterNamedService("pm:bm", std::make_shared<BootMode>(system), 4); // Nx = 4, Ams = 8
|
||||||
server_manager->RegisterNamedService("pm:dmnt", std::make_shared<DebugMonitor>(system));
|
server_manager->RegisterNamedService("pm:dmnt", std::make_shared<DebugMonitor>(system), 16);
|
||||||
server_manager->RegisterNamedService("pm:info", std::make_shared<Info>(system));
|
server_manager->RegisterNamedService("pm:shell", std::make_shared<Shell>(system), 3); //Nx = 3, AMS = 8
|
||||||
server_manager->RegisterNamedService("pm:shell", std::make_shared<Shell>(system));
|
server_manager->RegisterNamedService("pm:info", std::make_shared<Info>(system), 25); //48-(4+16+3)
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -593,9 +593,9 @@ void LoopProcess(Core::System& system) {
|
|||||||
return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User);
|
return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User);
|
||||||
};
|
};
|
||||||
|
|
||||||
server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser));
|
server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser), 2);
|
||||||
server_manager->RegisterNamedService("ro:1", std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin));
|
server_manager->RegisterNamedService("ro:1", std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin), 2);
|
||||||
server_manager->RegisterNamedService("ro:dmnt", std::make_shared<IDebugMonitorInterface>(system));
|
server_manager->RegisterNamedService("ro:dmnt", std::make_shared<IDebugMonitorInterface>(system), 2);
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -13,13 +16,10 @@ namespace Service::Set {
|
|||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("set", std::make_shared<ISettingsServer>(system));
|
server_manager->RegisterNamedService("set", std::make_shared<ISettingsServer>(system), 60);
|
||||||
server_manager->RegisterNamedService("set:cal",
|
server_manager->RegisterNamedService("set:cal", std::make_shared<IFactorySettingsServer>(system), 60);
|
||||||
std::make_shared<IFactorySettingsServer>(system));
|
server_manager->RegisterNamedService("set:fd", std::make_shared<IFirmwareDebugSettingsServer>(system), 60);
|
||||||
server_manager->RegisterNamedService("set:fd",
|
server_manager->RegisterNamedService("set:sys", std::make_shared<ISystemSettingsServer>(system), 60);
|
||||||
std::make_shared<IFirmwareDebugSettingsServer>(system));
|
|
||||||
server_manager->RegisterNamedService("set:sys",
|
|
||||||
std::make_shared<ISystemSettingsServer>(system));
|
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,7 @@ static Result ValidateServiceName(const std::string& name) {
|
|||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, std::string name,
|
Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, std::string name, u32 max_sessions, SessionRequestHandlerFactory handler) {
|
||||||
u32 max_sessions, SessionRequestHandlerFactory handler) {
|
|
||||||
R_TRY(ValidateServiceName(name));
|
R_TRY(ValidateServiceName(name));
|
||||||
|
|
||||||
std::scoped_lock lk{lock};
|
std::scoped_lock lk{lock};
|
||||||
@@ -64,7 +63,7 @@ Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, st
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto* port = Kernel::KPort::Create(kernel);
|
auto* port = Kernel::KPort::Create(kernel);
|
||||||
port->Initialize(kernel, ServerSessionCountMax, false, 0);
|
port->Initialize(kernel, max_sessions, false, 0);
|
||||||
|
|
||||||
// Register the port.
|
// Register the port.
|
||||||
Kernel::KPort::Register(kernel, port);
|
Kernel::KPort::Register(kernel, port);
|
||||||
@@ -264,7 +263,7 @@ void SM::AtmosphereHasService(HLERequestContext& ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SM::SM(ServiceManager& service_manager_, Core::System& system_)
|
SM::SM(ServiceManager& service_manager_, Core::System& system_)
|
||||||
: ServiceFramework{system_, "sm:", 4}
|
: ServiceFramework{system_, "sm:", 64}
|
||||||
, service_manager{service_manager_}
|
, service_manager{service_manager_}
|
||||||
, kernel{system_.Kernel()}
|
, kernel{system_.Kernel()}
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,17 +64,17 @@ public:
|
|||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("ethc:c", std::make_shared<ETHC_C>(system));
|
server_manager->RegisterNamedService("ethc:c", std::make_shared<ETHC_C>(system), 5);
|
||||||
server_manager->RegisterNamedService("ethc:i", std::make_shared<ETHC_I>(system));
|
server_manager->RegisterNamedService("ethc:i", std::make_shared<ETHC_I>(system), 5);
|
||||||
server_manager->RegisterNamedService("bsd:s", std::make_shared<BSD_USA>(system, "bsd:s", false));
|
server_manager->RegisterNamedService("bsd:s", std::make_shared<BSD_USA>(system, "bsd:s", false), 0x7E);
|
||||||
server_manager->RegisterNamedService("bsd:u", std::make_shared<BSD_USA>(system, "bsd:u", true));
|
server_manager->RegisterNamedService("bsd:u", std::make_shared<BSD_USA>(system, "bsd:u", true), 0x0f);
|
||||||
server_manager->RegisterNamedService("bsd:a", std::make_shared<BSD_USA>(system, "bsd:a", true));
|
server_manager->RegisterNamedService("bsd:a", std::make_shared<BSD_USA>(system, "bsd:a", true), 0x17);
|
||||||
server_manager->RegisterNamedService("bsd:nu", std::make_shared<BSD_NU>(system));
|
server_manager->RegisterNamedService("bsd:nu", std::make_shared<BSD_NU>(system), 4);
|
||||||
server_manager->RegisterNamedService("bsdcfg", std::make_shared<BSDCFG>(system, "bsdcfg"));
|
server_manager->RegisterNamedService("bsdcfg", std::make_shared<BSDCFG>(system, "bsdcfg"));
|
||||||
server_manager->RegisterNamedService("ifcfg", std::make_shared<BSDCFG>(system, "ifcfg"));
|
server_manager->RegisterNamedService("ifcfg", std::make_shared<BSDCFG>(system, "ifcfg"));
|
||||||
server_manager->RegisterNamedService("nsd:a", std::make_shared<NSD>(system, "nsd:a"));
|
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("nsd:u", std::make_shared<NSD>(system, "nsd:u"));
|
||||||
server_manager->RegisterNamedService("sfdnsres", std::make_shared<SFDNSRES>(system));
|
server_manager->RegisterNamedService("sfdnsres", std::make_shared<SFDNSRES>(system), 30);
|
||||||
server_manager->RegisterNamedService("dns:priv", std::make_shared<DNS_PRIV>(system));
|
server_manager->RegisterNamedService("dns:priv", std::make_shared<DNS_PRIV>(system));
|
||||||
server_manager->RegisterNamedService("eth:nd", std::make_shared<ISfDriverServiceCreator>(system));
|
server_manager->RegisterNamedService("eth:nd", std::make_shared<ISfDriverServiceCreator>(system));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,563 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "common/error.h"
|
||||||
|
#include "common/fs/file.h"
|
||||||
|
#include "common/hex_util.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
|
||||||
|
#include "core/hle/service/ssl/ssl_backend.h"
|
||||||
|
#include "core/internal_network/network.h"
|
||||||
|
#include "core/internal_network/sockets.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// These includes are inside the namespace to avoid a conflict on MinGW where
|
||||||
|
// the headers define an enum containing Network and Service as enumerators
|
||||||
|
// (which clash with the correspondingly named namespaces).
|
||||||
|
#define SECURITY_WIN32
|
||||||
|
#include <schnlsp.h>
|
||||||
|
#include <security.h>
|
||||||
|
#include <wincrypt.h>
|
||||||
|
|
||||||
|
std::once_flag one_time_init_flag;
|
||||||
|
bool one_time_init_success = false;
|
||||||
|
|
||||||
|
SCHANNEL_CRED schannel_cred{};
|
||||||
|
CredHandle cred_handle;
|
||||||
|
|
||||||
|
static void OneTimeInit() {
|
||||||
|
schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
|
||||||
|
schannel_cred.dwFlags =
|
||||||
|
SCH_USE_STRONG_CRYPTO | // don't allow insecure protocols
|
||||||
|
SCH_CRED_NO_SERVERNAME_CHECK | // don't validate server names
|
||||||
|
SCH_CRED_NO_DEFAULT_CREDS; // don't automatically present a client certificate
|
||||||
|
// ^ I'm assuming that nobody would want to connect Yuzu to a
|
||||||
|
// service that requires some OS-provided corporate client
|
||||||
|
// certificate, and presenting one to some arbitrary server
|
||||||
|
// might be a privacy concern? Who knows, though.
|
||||||
|
|
||||||
|
const SECURITY_STATUS ret =
|
||||||
|
AcquireCredentialsHandle(nullptr, const_cast<LPTSTR>(UNISP_NAME), SECPKG_CRED_OUTBOUND,
|
||||||
|
nullptr, &schannel_cred, nullptr, nullptr, &cred_handle, nullptr);
|
||||||
|
if (ret != SEC_E_OK) {
|
||||||
|
// SECURITY_STATUS codes are a type of HRESULT and can be used with NativeErrorToString.
|
||||||
|
LOG_ERROR(Service_SSL, "AcquireCredentialsHandle failed: {}",
|
||||||
|
Common::NativeErrorToString(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getenv("SSLKEYLOGFILE")) {
|
||||||
|
LOG_CRITICAL(Service_SSL, "SSLKEYLOGFILE was set but Schannel does not support exporting "
|
||||||
|
"keys; not logging keys!");
|
||||||
|
// Not fatal.
|
||||||
|
}
|
||||||
|
|
||||||
|
one_time_init_success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace Service::SSL {
|
||||||
|
|
||||||
|
class SSLConnectionBackendSchannel final : public SSLConnectionBackend {
|
||||||
|
public:
|
||||||
|
Result Init() {
|
||||||
|
std::call_once(one_time_init_flag, OneTimeInit);
|
||||||
|
|
||||||
|
if (!one_time_init_success) {
|
||||||
|
LOG_ERROR(
|
||||||
|
Service_SSL,
|
||||||
|
"Can't create SSL connection because Schannel one-time initialization failed");
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSocket(std::shared_ptr<Network::SocketBase> socket_in) override {
|
||||||
|
socket = std::move(socket_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SetHostName(const std::string& hostname_in) override {
|
||||||
|
hostname = hostname_in;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetVerifyOption(u32 option) override {
|
||||||
|
skip_cert_verification = (option == 0);
|
||||||
|
LOG_WARNING(Service_SSL, "option={} skip_verification={}", option,
|
||||||
|
skip_cert_verification);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoHandshake() override {
|
||||||
|
while (1) {
|
||||||
|
Result r;
|
||||||
|
switch (handshake_state) {
|
||||||
|
case HandshakeState::Initial:
|
||||||
|
if ((r = FlushCiphertextWriteBuf()) != ResultSuccess ||
|
||||||
|
(r = CallInitializeSecurityContext()) != ResultSuccess) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
// CallInitializeSecurityContext updated `handshake_state`.
|
||||||
|
continue;
|
||||||
|
case HandshakeState::ContinueNeeded:
|
||||||
|
case HandshakeState::IncompleteMessage:
|
||||||
|
if ((r = FlushCiphertextWriteBuf()) != ResultSuccess ||
|
||||||
|
(r = FillCiphertextReadBuf()) != ResultSuccess) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
if (ciphertext_read_buf.empty()) {
|
||||||
|
LOG_ERROR(Service_SSL, "SSL handshake failed because server hung up");
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
if ((r = CallInitializeSecurityContext()) != ResultSuccess) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
// CallInitializeSecurityContext updated `handshake_state`.
|
||||||
|
continue;
|
||||||
|
case HandshakeState::DoneAfterFlush:
|
||||||
|
if ((r = FlushCiphertextWriteBuf()) != ResultSuccess) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
handshake_state = HandshakeState::Connected;
|
||||||
|
return ResultSuccess;
|
||||||
|
case HandshakeState::Connected:
|
||||||
|
LOG_ERROR(Service_SSL, "Called DoHandshake but we already handshook");
|
||||||
|
return ResultInternalError;
|
||||||
|
case HandshakeState::Error:
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FillCiphertextReadBuf() {
|
||||||
|
const size_t fill_size = read_buf_fill_size ? read_buf_fill_size : 4096;
|
||||||
|
read_buf_fill_size = 0;
|
||||||
|
// This unnecessarily zeroes the buffer; oh well.
|
||||||
|
const size_t offset = ciphertext_read_buf.size();
|
||||||
|
ASSERT_OR_EXECUTE(offset + fill_size >= offset, { return ResultInternalError; });
|
||||||
|
ciphertext_read_buf.resize(offset + fill_size, 0);
|
||||||
|
const auto read_span = std::span(ciphertext_read_buf).subspan(offset, fill_size);
|
||||||
|
const auto [actual, err] = socket->Recv(0, read_span);
|
||||||
|
switch (err) {
|
||||||
|
case Network::Errno::SUCCESS:
|
||||||
|
ASSERT(static_cast<size_t>(actual) <= fill_size);
|
||||||
|
ciphertext_read_buf.resize(offset + actual);
|
||||||
|
return ResultSuccess;
|
||||||
|
case Network::Errno::AGAIN:
|
||||||
|
ciphertext_read_buf.resize(offset);
|
||||||
|
return ResultWouldBlock;
|
||||||
|
default:
|
||||||
|
ciphertext_read_buf.resize(offset);
|
||||||
|
LOG_ERROR(Service_SSL, "Socket recv returned Network::Errno {}", err);
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns success if the write buffer has been completely emptied.
|
||||||
|
Result FlushCiphertextWriteBuf() {
|
||||||
|
while (!ciphertext_write_buf.empty()) {
|
||||||
|
const auto [actual, err] = socket->Send(ciphertext_write_buf, 0);
|
||||||
|
switch (err) {
|
||||||
|
case Network::Errno::SUCCESS:
|
||||||
|
ASSERT(static_cast<size_t>(actual) <= ciphertext_write_buf.size());
|
||||||
|
ciphertext_write_buf.erase(ciphertext_write_buf.begin(),
|
||||||
|
ciphertext_write_buf.begin() + actual);
|
||||||
|
break;
|
||||||
|
case Network::Errno::AGAIN:
|
||||||
|
return ResultWouldBlock;
|
||||||
|
default:
|
||||||
|
LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err);
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CallInitializeSecurityContext() {
|
||||||
|
unsigned long req = ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY |
|
||||||
|
ISC_REQ_INTEGRITY | ISC_REQ_REPLAY_DETECT |
|
||||||
|
ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM |
|
||||||
|
ISC_REQ_USE_SUPPLIED_CREDS;
|
||||||
|
|
||||||
|
if (skip_cert_verification) {
|
||||||
|
req |= ISC_REQ_MANUAL_CRED_VALIDATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long attr;
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/secauthn/initializesecuritycontext--schannel
|
||||||
|
std::array<SecBuffer, 2> input_buffers{{
|
||||||
|
// only used if `initial_call_done`
|
||||||
|
{
|
||||||
|
// [0]
|
||||||
|
.cbBuffer = static_cast<unsigned long>(ciphertext_read_buf.size()),
|
||||||
|
.BufferType = SECBUFFER_TOKEN,
|
||||||
|
.pvBuffer = ciphertext_read_buf.data(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// [1] (will be replaced by SECBUFFER_MISSING when SEC_E_INCOMPLETE_MESSAGE is
|
||||||
|
// returned, or SECBUFFER_EXTRA when SEC_E_CONTINUE_NEEDED is returned if the
|
||||||
|
// whole buffer wasn't used)
|
||||||
|
.cbBuffer = 0,
|
||||||
|
.BufferType = SECBUFFER_EMPTY,
|
||||||
|
.pvBuffer = nullptr,
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
std::array<SecBuffer, 2> output_buffers{{
|
||||||
|
{
|
||||||
|
.cbBuffer = 0,
|
||||||
|
.BufferType = SECBUFFER_TOKEN,
|
||||||
|
.pvBuffer = nullptr,
|
||||||
|
}, // [0]
|
||||||
|
{
|
||||||
|
.cbBuffer = 0,
|
||||||
|
.BufferType = SECBUFFER_ALERT,
|
||||||
|
.pvBuffer = nullptr,
|
||||||
|
}, // [1]
|
||||||
|
}};
|
||||||
|
SecBufferDesc input_desc{
|
||||||
|
.ulVersion = SECBUFFER_VERSION,
|
||||||
|
.cBuffers = static_cast<unsigned long>(input_buffers.size()),
|
||||||
|
.pBuffers = input_buffers.data(),
|
||||||
|
};
|
||||||
|
SecBufferDesc output_desc{
|
||||||
|
.ulVersion = SECBUFFER_VERSION,
|
||||||
|
.cBuffers = static_cast<unsigned long>(output_buffers.size()),
|
||||||
|
.pBuffers = output_buffers.data(),
|
||||||
|
};
|
||||||
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
|
input_buffers[0].cbBuffer == ciphertext_read_buf.size(),
|
||||||
|
{ return ResultInternalError; }, "read buffer too large");
|
||||||
|
|
||||||
|
bool initial_call_done = handshake_state != HandshakeState::Initial;
|
||||||
|
if (initial_call_done) {
|
||||||
|
LOG_DEBUG(Service_SSL, "Passing {} bytes into InitializeSecurityContext",
|
||||||
|
ciphertext_read_buf.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
char* hostname_ptr = hostname ? const_cast<char*>(hostname->c_str()) : nullptr;
|
||||||
|
const SECURITY_STATUS ret = InitializeSecurityContextA(
|
||||||
|
&cred_handle, initial_call_done ? &ctxt : nullptr, hostname_ptr, req,
|
||||||
|
0, // Reserved1
|
||||||
|
0, // TargetDataRep not used with Schannel
|
||||||
|
initial_call_done ? &input_desc : nullptr,
|
||||||
|
0, // Reserved2
|
||||||
|
initial_call_done ? nullptr : &ctxt, &output_desc, &attr,
|
||||||
|
nullptr); // ptsExpiry
|
||||||
|
|
||||||
|
if (output_buffers[0].pvBuffer) {
|
||||||
|
const std::span span(static_cast<u8*>(output_buffers[0].pvBuffer),
|
||||||
|
output_buffers[0].cbBuffer);
|
||||||
|
ciphertext_write_buf.insert(ciphertext_write_buf.end(), span.begin(), span.end());
|
||||||
|
FreeContextBuffer(output_buffers[0].pvBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output_buffers[1].pvBuffer) {
|
||||||
|
const std::span span(static_cast<u8*>(output_buffers[1].pvBuffer),
|
||||||
|
output_buffers[1].cbBuffer);
|
||||||
|
// The documentation doesn't explain what format this data is in.
|
||||||
|
LOG_DEBUG(Service_SSL, "Got a {}-byte alert buffer: {}", span.size(),
|
||||||
|
Common::HexToString(span));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ret) {
|
||||||
|
case SEC_I_CONTINUE_NEEDED:
|
||||||
|
LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_I_CONTINUE_NEEDED");
|
||||||
|
if (input_buffers[1].BufferType == SECBUFFER_EXTRA) {
|
||||||
|
LOG_DEBUG(Service_SSL, "EXTRA of size {}", input_buffers[1].cbBuffer);
|
||||||
|
ASSERT(input_buffers[1].cbBuffer <= ciphertext_read_buf.size());
|
||||||
|
ciphertext_read_buf.erase(ciphertext_read_buf.begin(),
|
||||||
|
ciphertext_read_buf.end() - input_buffers[1].cbBuffer);
|
||||||
|
} else {
|
||||||
|
ASSERT(input_buffers[1].BufferType == SECBUFFER_EMPTY);
|
||||||
|
ciphertext_read_buf.clear();
|
||||||
|
}
|
||||||
|
handshake_state = HandshakeState::ContinueNeeded;
|
||||||
|
return ResultSuccess;
|
||||||
|
case SEC_E_INCOMPLETE_MESSAGE:
|
||||||
|
LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_E_INCOMPLETE_MESSAGE");
|
||||||
|
ASSERT(input_buffers[1].BufferType == SECBUFFER_MISSING);
|
||||||
|
read_buf_fill_size = input_buffers[1].cbBuffer;
|
||||||
|
handshake_state = HandshakeState::IncompleteMessage;
|
||||||
|
return ResultSuccess;
|
||||||
|
case SEC_E_OK:
|
||||||
|
LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_E_OK");
|
||||||
|
ciphertext_read_buf.clear();
|
||||||
|
handshake_state = HandshakeState::DoneAfterFlush;
|
||||||
|
return GrabStreamSizes();
|
||||||
|
default:
|
||||||
|
LOG_ERROR(Service_SSL,
|
||||||
|
"InitializeSecurityContext failed (probably certificate/protocol issue): {}",
|
||||||
|
Common::NativeErrorToString(ret));
|
||||||
|
handshake_state = HandshakeState::Error;
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GrabStreamSizes() {
|
||||||
|
const SECURITY_STATUS ret =
|
||||||
|
QueryContextAttributes(&ctxt, SECPKG_ATTR_STREAM_SIZES, &stream_sizes);
|
||||||
|
if (ret != SEC_E_OK) {
|
||||||
|
LOG_ERROR(Service_SSL, "QueryContextAttributes(SECPKG_ATTR_STREAM_SIZES) failed: {}",
|
||||||
|
Common::NativeErrorToString(ret));
|
||||||
|
handshake_state = HandshakeState::Error;
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Read(size_t* out_size, std::span<u8> data) override {
|
||||||
|
*out_size = 0;
|
||||||
|
if (handshake_state != HandshakeState::Connected) {
|
||||||
|
LOG_ERROR(Service_SSL, "Called Read but we did not successfully handshake");
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
if (data.size() == 0 || got_read_eof) {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
if (!cleartext_read_buf.empty()) {
|
||||||
|
*out_size = (std::min)(cleartext_read_buf.size(), data.size());
|
||||||
|
std::memcpy(data.data(), cleartext_read_buf.data(), *out_size);
|
||||||
|
cleartext_read_buf.erase(cleartext_read_buf.begin(),
|
||||||
|
cleartext_read_buf.begin() + *out_size);
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
if (!ciphertext_read_buf.empty()) {
|
||||||
|
SecBuffer empty{
|
||||||
|
.cbBuffer = 0,
|
||||||
|
.BufferType = SECBUFFER_EMPTY,
|
||||||
|
.pvBuffer = nullptr,
|
||||||
|
};
|
||||||
|
std::array<SecBuffer, 5> buffers{{
|
||||||
|
{
|
||||||
|
.cbBuffer = static_cast<unsigned long>(ciphertext_read_buf.size()),
|
||||||
|
.BufferType = SECBUFFER_DATA,
|
||||||
|
.pvBuffer = ciphertext_read_buf.data(),
|
||||||
|
},
|
||||||
|
empty,
|
||||||
|
empty,
|
||||||
|
empty,
|
||||||
|
}};
|
||||||
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
|
buffers[0].cbBuffer == ciphertext_read_buf.size(),
|
||||||
|
{ return ResultInternalError; }, "read buffer too large");
|
||||||
|
SecBufferDesc desc{
|
||||||
|
.ulVersion = SECBUFFER_VERSION,
|
||||||
|
.cBuffers = static_cast<unsigned long>(buffers.size()),
|
||||||
|
.pBuffers = buffers.data(),
|
||||||
|
};
|
||||||
|
SECURITY_STATUS ret =
|
||||||
|
DecryptMessage(&ctxt, &desc, /*MessageSeqNo*/ 0, /*pfQOP*/ nullptr);
|
||||||
|
switch (ret) {
|
||||||
|
case SEC_E_OK:
|
||||||
|
ASSERT_OR_EXECUTE(buffers[0].BufferType == SECBUFFER_STREAM_HEADER,
|
||||||
|
{ return ResultInternalError; });
|
||||||
|
ASSERT_OR_EXECUTE(buffers[1].BufferType == SECBUFFER_DATA,
|
||||||
|
{ return ResultInternalError; });
|
||||||
|
ASSERT_OR_EXECUTE(buffers[2].BufferType == SECBUFFER_STREAM_TRAILER,
|
||||||
|
{ return ResultInternalError; });
|
||||||
|
cleartext_read_buf.assign(static_cast<u8*>(buffers[1].pvBuffer),
|
||||||
|
static_cast<u8*>(buffers[1].pvBuffer) +
|
||||||
|
buffers[1].cbBuffer);
|
||||||
|
if (buffers[3].BufferType == SECBUFFER_EXTRA) {
|
||||||
|
ASSERT(buffers[3].cbBuffer <= ciphertext_read_buf.size());
|
||||||
|
ciphertext_read_buf.erase(ciphertext_read_buf.begin(),
|
||||||
|
ciphertext_read_buf.end() - buffers[3].cbBuffer);
|
||||||
|
} else {
|
||||||
|
ASSERT(buffers[3].BufferType == SECBUFFER_EMPTY);
|
||||||
|
ciphertext_read_buf.clear();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case SEC_E_INCOMPLETE_MESSAGE:
|
||||||
|
break;
|
||||||
|
case SEC_I_CONTEXT_EXPIRED:
|
||||||
|
// Server hung up by sending close_notify.
|
||||||
|
got_read_eof = true;
|
||||||
|
*out_size = 0;
|
||||||
|
return ResultSuccess;
|
||||||
|
default:
|
||||||
|
LOG_ERROR(Service_SSL, "DecryptMessage failed: {}",
|
||||||
|
Common::NativeErrorToString(ret));
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const Result r = FillCiphertextReadBuf();
|
||||||
|
if (r != ResultSuccess) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
if (ciphertext_read_buf.empty()) {
|
||||||
|
got_read_eof = true;
|
||||||
|
*out_size = 0;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Write(size_t* out_size, std::span<const u8> data) override {
|
||||||
|
*out_size = 0;
|
||||||
|
|
||||||
|
if (handshake_state != HandshakeState::Connected) {
|
||||||
|
LOG_ERROR(Service_SSL, "Called Write but we did not successfully handshake");
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
if (data.size() == 0) {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
data = data.subspan(0, std::min<size_t>(data.size(), stream_sizes.cbMaximumMessage));
|
||||||
|
if (!cleartext_write_buf.empty()) {
|
||||||
|
// Already in the middle of a write. It wouldn't make sense to not
|
||||||
|
// finish sending the entire buffer since TLS has
|
||||||
|
// header/MAC/padding/etc.
|
||||||
|
if (data.size() != cleartext_write_buf.size() ||
|
||||||
|
std::memcmp(data.data(), cleartext_write_buf.data(), data.size())) {
|
||||||
|
LOG_ERROR(Service_SSL, "Called Write but buffer does not match previous buffer");
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
return WriteAlreadyEncryptedData(out_size);
|
||||||
|
} else {
|
||||||
|
cleartext_write_buf.assign(data.begin(), data.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> header_buf(stream_sizes.cbHeader, 0);
|
||||||
|
std::vector<u8> tmp_data_buf = cleartext_write_buf;
|
||||||
|
std::vector<u8> trailer_buf(stream_sizes.cbTrailer, 0);
|
||||||
|
|
||||||
|
std::array<SecBuffer, 3> buffers{{
|
||||||
|
{
|
||||||
|
.cbBuffer = stream_sizes.cbHeader,
|
||||||
|
.BufferType = SECBUFFER_STREAM_HEADER,
|
||||||
|
.pvBuffer = header_buf.data(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.cbBuffer = static_cast<unsigned long>(tmp_data_buf.size()),
|
||||||
|
.BufferType = SECBUFFER_DATA,
|
||||||
|
.pvBuffer = tmp_data_buf.data(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.cbBuffer = stream_sizes.cbTrailer,
|
||||||
|
.BufferType = SECBUFFER_STREAM_TRAILER,
|
||||||
|
.pvBuffer = trailer_buf.data(),
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
|
buffers[1].cbBuffer == tmp_data_buf.size(), { return ResultInternalError; },
|
||||||
|
"temp buffer too large");
|
||||||
|
SecBufferDesc desc{
|
||||||
|
.ulVersion = SECBUFFER_VERSION,
|
||||||
|
.cBuffers = static_cast<unsigned long>(buffers.size()),
|
||||||
|
.pBuffers = buffers.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const SECURITY_STATUS ret = EncryptMessage(&ctxt, /*fQOP*/ 0, &desc, /*MessageSeqNo*/ 0);
|
||||||
|
if (ret != SEC_E_OK) {
|
||||||
|
LOG_ERROR(Service_SSL, "EncryptMessage failed: {}", Common::NativeErrorToString(ret));
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
ciphertext_write_buf.insert(ciphertext_write_buf.end(), header_buf.begin(),
|
||||||
|
header_buf.end());
|
||||||
|
ciphertext_write_buf.insert(ciphertext_write_buf.end(), tmp_data_buf.begin(),
|
||||||
|
tmp_data_buf.end());
|
||||||
|
ciphertext_write_buf.insert(ciphertext_write_buf.end(), trailer_buf.begin(),
|
||||||
|
trailer_buf.end());
|
||||||
|
return WriteAlreadyEncryptedData(out_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result WriteAlreadyEncryptedData(size_t* out_size) {
|
||||||
|
const Result r = FlushCiphertextWriteBuf();
|
||||||
|
if (r != ResultSuccess) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
// write buf is empty
|
||||||
|
*out_size = cleartext_write_buf.size();
|
||||||
|
cleartext_write_buf.clear();
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) override {
|
||||||
|
PCCERT_CONTEXT returned_cert = nullptr;
|
||||||
|
const SECURITY_STATUS ret =
|
||||||
|
QueryContextAttributes(&ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &returned_cert);
|
||||||
|
if (ret != SEC_E_OK) {
|
||||||
|
LOG_ERROR(Service_SSL,
|
||||||
|
"QueryContextAttributes(SECPKG_ATTR_REMOTE_CERT_CONTEXT) failed: {}",
|
||||||
|
Common::NativeErrorToString(ret));
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
PCCERT_CONTEXT some_cert = nullptr;
|
||||||
|
while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert)) !=
|
||||||
|
nullptr) {
|
||||||
|
out_certs->emplace_back(static_cast<u8*>(some_cert->pbCertEncoded),
|
||||||
|
static_cast<u8*>(some_cert->pbCertEncoded) +
|
||||||
|
some_cert->cbCertEncoded);
|
||||||
|
}
|
||||||
|
std::reverse(out_certs->begin(),
|
||||||
|
out_certs->end()); // Windows returns certs in reverse order from what we want
|
||||||
|
CertFreeCertificateContext(returned_cert);
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
~SSLConnectionBackendSchannel() {
|
||||||
|
if (handshake_state != HandshakeState::Initial) {
|
||||||
|
DeleteSecurityContext(&ctxt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class HandshakeState {
|
||||||
|
// Haven't called anything yet.
|
||||||
|
Initial,
|
||||||
|
// `SEC_I_CONTINUE_NEEDED` was returned by
|
||||||
|
// `InitializeSecurityContext`; must finish sending data (if any) in
|
||||||
|
// the write buffer, then read at least one byte before calling
|
||||||
|
// `InitializeSecurityContext` again.
|
||||||
|
ContinueNeeded,
|
||||||
|
// `SEC_E_INCOMPLETE_MESSAGE` was returned by
|
||||||
|
// `InitializeSecurityContext`; hopefully the write buffer is empty;
|
||||||
|
// must read at least one byte before calling
|
||||||
|
// `InitializeSecurityContext` again.
|
||||||
|
IncompleteMessage,
|
||||||
|
// `SEC_E_OK` was returned by `InitializeSecurityContext`; must
|
||||||
|
// finish sending data in the write buffer before having `DoHandshake`
|
||||||
|
// report success.
|
||||||
|
DoneAfterFlush,
|
||||||
|
// We finished the above and are now connected. At this point, writing
|
||||||
|
// and reading are separate 'state machines' represented by the
|
||||||
|
// nonemptiness of the ciphertext and cleartext read and write buffers.
|
||||||
|
Connected,
|
||||||
|
// Another error was returned and we shouldn't allow initialization
|
||||||
|
// to continue.
|
||||||
|
Error,
|
||||||
|
} handshake_state = HandshakeState::Initial;
|
||||||
|
|
||||||
|
CtxtHandle ctxt;
|
||||||
|
SecPkgContext_StreamSizes stream_sizes;
|
||||||
|
|
||||||
|
std::shared_ptr<Network::SocketBase> socket;
|
||||||
|
std::optional<std::string> hostname;
|
||||||
|
|
||||||
|
std::vector<u8> ciphertext_read_buf;
|
||||||
|
std::vector<u8> ciphertext_write_buf;
|
||||||
|
std::vector<u8> cleartext_read_buf;
|
||||||
|
std::vector<u8> cleartext_write_buf;
|
||||||
|
|
||||||
|
bool got_read_eof = false;
|
||||||
|
bool skip_cert_verification = false;
|
||||||
|
size_t read_buf_fill_size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend) {
|
||||||
|
auto conn = std::make_unique<SSLConnectionBackendSchannel>();
|
||||||
|
|
||||||
|
R_TRY(conn->Init());
|
||||||
|
|
||||||
|
*out_backend = std::move(conn);
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::SSL
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
// SecureTransport has been deprecated in its entirety in favor of
|
||||||
|
// Network.framework, but that does not allow layering TLS on top of an
|
||||||
|
// arbitrary socket.
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
#include <Security/SecureTransport.h>
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "core/hle/service/ssl/ssl_backend.h"
|
||||||
|
#include "core/internal_network/network.h"
|
||||||
|
#include "core/internal_network/sockets.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct CFReleaser {
|
||||||
|
T ptr;
|
||||||
|
|
||||||
|
YUZU_NON_COPYABLE(CFReleaser);
|
||||||
|
constexpr CFReleaser() : ptr(nullptr) {}
|
||||||
|
constexpr CFReleaser(T ptr) : ptr(ptr) {}
|
||||||
|
constexpr operator T() {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
~CFReleaser() {
|
||||||
|
if (ptr) {
|
||||||
|
CFRelease(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string CFStringToString(CFStringRef cfstr) {
|
||||||
|
CFReleaser<CFDataRef> cfdata(
|
||||||
|
CFStringCreateExternalRepresentation(nullptr, cfstr, kCFStringEncodingUTF8, 0));
|
||||||
|
ASSERT_OR_EXECUTE(cfdata, { return "???"; });
|
||||||
|
return std::string(reinterpret_cast<const char*>(CFDataGetBytePtr(cfdata)),
|
||||||
|
CFDataGetLength(cfdata));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OSStatusToString(OSStatus status) {
|
||||||
|
CFReleaser<CFStringRef> cfstr(SecCopyErrorMessageString(status, nullptr));
|
||||||
|
if (!cfstr) {
|
||||||
|
return "[unknown error]";
|
||||||
|
}
|
||||||
|
return CFStringToString(cfstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace Service::SSL {
|
||||||
|
|
||||||
|
class SSLConnectionBackendSecureTransport final : public SSLConnectionBackend {
|
||||||
|
public:
|
||||||
|
Result Init() {
|
||||||
|
static std::once_flag once_flag;
|
||||||
|
std::call_once(once_flag, []() {
|
||||||
|
if (getenv("SSLKEYLOGFILE")) {
|
||||||
|
LOG_CRITICAL(Service_SSL, "SSLKEYLOGFILE was set but SecureTransport does not "
|
||||||
|
"support exporting keys; not logging keys!");
|
||||||
|
// Not fatal.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
context.ptr = SSLCreateContext(nullptr, kSSLClientSide, kSSLStreamType);
|
||||||
|
if (!context) {
|
||||||
|
LOG_ERROR(Service_SSL, "SSLCreateContext failed");
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus status;
|
||||||
|
if ((status = SSLSetIOFuncs(context, ReadCallback, WriteCallback)) ||
|
||||||
|
(status = SSLSetConnection(context, this))) {
|
||||||
|
LOG_ERROR(Service_SSL, "SSLContext initialization failed: {}",
|
||||||
|
OSStatusToString(status));
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSocket(std::shared_ptr<Network::SocketBase> in_socket) override {
|
||||||
|
socket = std::move(in_socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SetHostName(const std::string& hostname) override {
|
||||||
|
OSStatus status = SSLSetPeerDomainName(context, hostname.c_str(), hostname.size());
|
||||||
|
if (status) {
|
||||||
|
LOG_ERROR(Service_SSL, "SSLSetPeerDomainName failed: {}", OSStatusToString(status));
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetVerifyOption(u32 option) override {
|
||||||
|
skip_cert_verification = (option == 0);
|
||||||
|
LOG_WARNING(Service_SSL, "option={} skip_verification={}", option,
|
||||||
|
skip_cert_verification);
|
||||||
|
if (skip_cert_verification) {
|
||||||
|
SSLSetSessionOption(context, kSSLSessionOptionBreakOnServerAuth, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DoHandshake() override {
|
||||||
|
OSStatus status = SSLHandshake(context);
|
||||||
|
|
||||||
|
if (skip_cert_verification && status == errSSLServerAuthCompleted) {
|
||||||
|
LOG_DEBUG(Service_SSL, "Skipping certificate verification as requested");
|
||||||
|
status = SSLHandshake(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return HandleReturn("SSLHandshake", 0, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Read(size_t* out_size, std::span<u8> data) override {
|
||||||
|
OSStatus status = SSLRead(context, data.data(), data.size(), out_size);
|
||||||
|
return HandleReturn("SSLRead", out_size, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Write(size_t* out_size, std::span<const u8> data) override {
|
||||||
|
OSStatus status = SSLWrite(context, data.data(), data.size(), out_size);
|
||||||
|
return HandleReturn("SSLWrite", out_size, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result HandleReturn(const char* what, size_t* actual, OSStatus status) {
|
||||||
|
switch (status) {
|
||||||
|
case 0:
|
||||||
|
return ResultSuccess;
|
||||||
|
case errSSLWouldBlock:
|
||||||
|
return ResultWouldBlock;
|
||||||
|
default: {
|
||||||
|
std::string reason;
|
||||||
|
if (got_read_eof) {
|
||||||
|
reason = "server hung up";
|
||||||
|
} else {
|
||||||
|
reason = OSStatusToString(status);
|
||||||
|
}
|
||||||
|
LOG_ERROR(Service_SSL, "{} failed: {}", what, reason);
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) override {
|
||||||
|
CFReleaser<SecTrustRef> trust;
|
||||||
|
OSStatus status = SSLCopyPeerTrust(context, &trust.ptr);
|
||||||
|
if (status) {
|
||||||
|
LOG_ERROR(Service_SSL, "SSLCopyPeerTrust failed: {}", OSStatusToString(status));
|
||||||
|
return ResultInternalError;
|
||||||
|
}
|
||||||
|
for (CFIndex i = 0, count = SecTrustGetCertificateCount(trust); i < count; i++) {
|
||||||
|
SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i);
|
||||||
|
CFReleaser<CFDataRef> data(SecCertificateCopyData(cert));
|
||||||
|
ASSERT_OR_EXECUTE(data, { return ResultInternalError; });
|
||||||
|
const u8* ptr = CFDataGetBytePtr(data);
|
||||||
|
out_certs->emplace_back(ptr, ptr + CFDataGetLength(data));
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OSStatus ReadCallback(SSLConnectionRef connection, void* data, size_t* dataLength) {
|
||||||
|
return ReadOrWriteCallback(connection, data, dataLength, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static OSStatus WriteCallback(SSLConnectionRef connection, const void* data,
|
||||||
|
size_t* dataLength) {
|
||||||
|
return ReadOrWriteCallback(connection, const_cast<void*>(data), dataLength, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static OSStatus ReadOrWriteCallback(SSLConnectionRef connection, void* data, size_t* dataLength,
|
||||||
|
bool is_read) {
|
||||||
|
auto self =
|
||||||
|
static_cast<SSLConnectionBackendSecureTransport*>(const_cast<void*>(connection));
|
||||||
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
|
self->socket, { return 0; }, "SecureTransport asked to {} but we have no socket",
|
||||||
|
is_read ? "read" : "write");
|
||||||
|
|
||||||
|
// SecureTransport callbacks (unlike OpenSSL BIO callbacks) are
|
||||||
|
// expected to read/write the full requested dataLength or return an
|
||||||
|
// error, so we have to add a loop ourselves.
|
||||||
|
size_t requested_len = *dataLength;
|
||||||
|
size_t offset = 0;
|
||||||
|
while (offset < requested_len) {
|
||||||
|
std::span cur(reinterpret_cast<u8*>(data) + offset, requested_len - offset);
|
||||||
|
auto [actual, err] = is_read ? self->socket->Recv(0, cur) : self->socket->Send(cur, 0);
|
||||||
|
LOG_CRITICAL(Service_SSL, "op={}, offset={} actual={}/{} err={}", is_read, offset,
|
||||||
|
actual, cur.size(), static_cast<s32>(err));
|
||||||
|
switch (err) {
|
||||||
|
case Network::Errno::SUCCESS:
|
||||||
|
offset += actual;
|
||||||
|
if (actual == 0) {
|
||||||
|
ASSERT(is_read);
|
||||||
|
self->got_read_eof = true;
|
||||||
|
return errSecEndOfData;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Network::Errno::AGAIN:
|
||||||
|
*dataLength = offset;
|
||||||
|
return errSSLWouldBlock;
|
||||||
|
default:
|
||||||
|
LOG_ERROR(Service_SSL, "Socket {} returned Network::Errno {}",
|
||||||
|
is_read ? "recv" : "send", err);
|
||||||
|
return errSecIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT(offset == requested_len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CFReleaser<SSLContextRef> context = nullptr;
|
||||||
|
bool got_read_eof = false;
|
||||||
|
bool skip_cert_verification = false;
|
||||||
|
|
||||||
|
std::shared_ptr<Network::SocketBase> socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend) {
|
||||||
|
auto conn = std::make_unique<SSLConnectionBackendSecureTransport>();
|
||||||
|
|
||||||
|
R_TRY(conn->Init());
|
||||||
|
|
||||||
|
*out_backend = std::move(conn);
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::SSL
|
||||||
@@ -265,17 +265,17 @@ void LoopProcess(Core::System& system) {
|
|||||||
|
|
||||||
server_manager->RegisterNamedService("usb:ds", std::make_shared<IDsRootSession>(system));
|
server_manager->RegisterNamedService("usb:ds", std::make_shared<IDsRootSession>(system));
|
||||||
server_manager->RegisterNamedService("usb:hs", std::make_shared<IClientRootSession>(system));
|
server_manager->RegisterNamedService("usb:hs", std::make_shared<IClientRootSession>(system));
|
||||||
server_manager->RegisterNamedService("usb:pd", std::make_shared<IPdManager>(system));
|
server_manager->RegisterNamedService("usb:pd", std::make_shared<IPdManager>(system), 6);
|
||||||
server_manager->RegisterNamedService("usb:pd:c", std::make_shared<IPdCradleManager>(system));
|
server_manager->RegisterNamedService("usb:pd:c", std::make_shared<IPdCradleManager>(system), 4);
|
||||||
server_manager->RegisterNamedService("usb:pd:m", std::make_shared<IPdManufactureManager>(system));
|
server_manager->RegisterNamedService("usb:pd:m", std::make_shared<IPdManufactureManager>(system));
|
||||||
server_manager->RegisterNamedService("usb:pm", std::make_shared<IPmMainService>(system));
|
server_manager->RegisterNamedService("usb:pm", std::make_shared<IPmMainService>(system), 5);
|
||||||
// +7.0.0
|
// +7.0.0
|
||||||
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 7) {
|
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 7) {
|
||||||
server_manager->RegisterNamedService("usb:qdb", std::make_shared<IQdbManager>(system));
|
server_manager->RegisterNamedService("usb:qdb", std::make_shared<IQdbManager>(system));
|
||||||
}
|
}
|
||||||
// +8.0.0
|
// +8.0.0
|
||||||
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 8) {
|
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 8) {
|
||||||
server_manager->RegisterNamedService("usb:obsv", std::make_shared<IPmObserverService>(system));
|
server_manager->RegisterNamedService("usb:obsv", std::make_shared<IPmObserverService>(system), 2);
|
||||||
}
|
}
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,14 +246,14 @@ public:
|
|||||||
|
|
||||||
void LoopProcess(Core::System& system) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
server_manager->RegisterNamedService("wlan:lcl", std::make_shared<ILocalManager>(system));
|
server_manager->RegisterNamedService("wlan:lcl", std::make_shared<ILocalManager>(system), 10);
|
||||||
server_manager->RegisterNamedService("wlan:lg", std::make_shared<ILocalGetFrame>(system));
|
server_manager->RegisterNamedService("wlan:lg", std::make_shared<ILocalGetFrame>(system), 10);
|
||||||
server_manager->RegisterNamedService("wlan:lga", std::make_shared<ILocalGetActionFrame>(system));
|
server_manager->RegisterNamedService("wlan:lga", std::make_shared<ILocalGetActionFrame>(system), 10);
|
||||||
server_manager->RegisterNamedService("wlan:sg", std::make_shared<ISocketGetFrame>(system));
|
server_manager->RegisterNamedService("wlan:sg", std::make_shared<ISocketGetFrame>(system), 10);
|
||||||
server_manager->RegisterNamedService("wlan:soc", std::make_shared<ISocketManager>(system));
|
server_manager->RegisterNamedService("wlan:soc", std::make_shared<ISocketManager>(system), 10);
|
||||||
server_manager->RegisterNamedService("wlan:dtc", std::make_shared<IDetectManager>(system));
|
server_manager->RegisterNamedService("wlan:dtc", std::make_shared<IDetectManager>(system), 4);
|
||||||
server_manager->RegisterNamedService("wlan:p", std::make_shared<IPrivateServiceCreator>(system));
|
server_manager->RegisterNamedService("wlan:p", std::make_shared<IPrivateServiceCreator>(system), 30);
|
||||||
server_manager->RegisterNamedService("wlan:nd", std::make_shared<ISfDriverServiceCreator>(system));
|
server_manager->RegisterNamedService("wlan:nd", std::make_shared<ISfDriverServiceCreator>(system), 5);
|
||||||
ServerManager::RunServer(std::move(server_manager));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ constexpr u64 GpuClockMultiplier(Settings::GpuClock clock) {
|
|||||||
|
|
||||||
struct GPU::Impl {
|
struct GPU::Impl {
|
||||||
explicit Impl(Core::System& system_, bool is_async_, bool use_nvdec_)
|
explicit Impl(Core::System& system_, bool is_async_, bool use_nvdec_)
|
||||||
: gpu_thread{system_}
|
: system{system_}
|
||||||
, system{system_}
|
, gpu_thread{system_}
|
||||||
, use_nvdec{use_nvdec_}
|
, use_nvdec{use_nvdec_}
|
||||||
, shader_notify()
|
, shader_notify()
|
||||||
, is_async{is_async_}
|
, is_async{is_async_}
|
||||||
@@ -182,6 +182,7 @@ struct GPU::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NotifyShutdown() {
|
void NotifyShutdown() {
|
||||||
|
gpu_thread.NotifyShutdown();
|
||||||
std::unique_lock lk{sync_mutex};
|
std::unique_lock lk{sync_mutex};
|
||||||
shutting_down.store(true, std::memory_order::relaxed);
|
shutting_down.store(true, std::memory_order::relaxed);
|
||||||
sync_cv.notify_all();
|
sync_cv.notify_all();
|
||||||
@@ -301,12 +302,12 @@ struct GPU::Impl {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Core::System& system;
|
||||||
|
|
||||||
// Destruction of thread must be done before all (non trivial)
|
// Destruction of thread must be done before all (non trivial)
|
||||||
// previous members has been destroyed
|
// previous members has been destroyed
|
||||||
VideoCommon::GPUThread::ThreadManager gpu_thread;
|
VideoCommon::GPUThread::ThreadManager gpu_thread;
|
||||||
|
|
||||||
Core::System& system;
|
|
||||||
|
|
||||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||||
const bool use_nvdec;
|
const bool use_nvdec;
|
||||||
|
|
||||||
|
|||||||
@@ -114,4 +114,11 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block, bool is_a
|
|||||||
return fence;
|
return fence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ThreadManager::NotifyShutdown() {
|
||||||
|
if (thread.joinable()) {
|
||||||
|
thread.request_stop();
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace VideoCommon::GPUThread
|
} // namespace VideoCommon::GPUThread
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ public:
|
|||||||
|
|
||||||
void TickGPU(bool is_async);
|
void TickGPU(bool is_async);
|
||||||
|
|
||||||
|
void NotifyShutdown();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Pushes a command to be executed by the GPU thread
|
/// Pushes a command to be executed by the GPU thread
|
||||||
u64 PushCommand(CommandData&& command_data, bool block, bool is_async);
|
u64 PushCommand(CommandData&& command_data, bool block, bool is_async);
|
||||||
|
|||||||
Reference in New Issue
Block a user