Merge b22471fb46
into dc20a61527
This commit is contained in:
commit
82afbc604a
|
@ -376,6 +376,8 @@ add_library(skyline SHARED
|
|||
${source_DIR}/skyline/services/account/IAccountServiceForApplication.cpp
|
||||
${source_DIR}/skyline/services/account/IManagerForApplication.cpp
|
||||
${source_DIR}/skyline/services/account/IProfile.cpp
|
||||
${source_DIR}/skyline/services/account/IAsyncContext.cpp
|
||||
${source_DIR}/skyline/services/account/IAuthorizationRequest.cpp
|
||||
${source_DIR}/skyline/services/friends/IServiceCreator.cpp
|
||||
${source_DIR}/skyline/services/friends/IFriendService.cpp
|
||||
${source_DIR}/skyline/services/friends/INotificationService.cpp
|
||||
|
@ -400,6 +402,7 @@ add_library(skyline SHARED
|
|||
${source_DIR}/skyline/services/mii/IStaticService.cpp
|
||||
${source_DIR}/skyline/services/mii/IDatabaseService.cpp
|
||||
${source_DIR}/skyline/services/olsc/IOlscServiceForApplication.cpp
|
||||
${source_DIR}/skyline/services/ntc/IEnsureNetworkClockAvailabilityService.cpp
|
||||
)
|
||||
target_include_directories(skyline PRIVATE ${source_DIR}/skyline)
|
||||
# target_precompile_headers(skyline PRIVATE ${source_DIR}/skyline/common.h) # PCH will currently break Intellisense
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<uses-feature
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace skyline {
|
|||
profilePictureValue = ktSettings.GetString("profilePictureValue");
|
||||
systemLanguage = ktSettings.GetInt<skyline::language::SystemLanguage>("systemLanguage");
|
||||
systemRegion = ktSettings.GetInt<skyline::region::RegionCode>("systemRegion");
|
||||
isInternetEnabled = ktSettings.GetBool("isInternetEnabled");
|
||||
forceTripleBuffering = ktSettings.GetBool("forceTripleBuffering");
|
||||
disableFrameThrottling = ktSettings.GetBool("disableFrameThrottling");
|
||||
gpuDriver = ktSettings.GetString("gpuDriver");
|
||||
|
|
|
@ -64,6 +64,7 @@ namespace skyline {
|
|||
Setting<std::string> profilePictureValue; //!< The profile picture path to be supplied to the guest
|
||||
Setting<language::SystemLanguage> systemLanguage; //!< The system language
|
||||
Setting<region::RegionCode> systemRegion; //!< The system region
|
||||
Setting<bool> isInternetEnabled; //!< If emulator uses internet
|
||||
|
||||
// Display
|
||||
Setting<bool> forceTripleBuffering; //!< If the presentation engine should always triple buffer even if the swapchain supports double buffering
|
||||
|
|
|
@ -64,7 +64,8 @@ namespace skyline {
|
|||
closeKeyboardId{environ->GetMethodID(instanceClass, "closeKeyboard", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;)V")},
|
||||
showValidationResultId{environ->GetMethodID(instanceClass, "showValidationResult", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;ILjava/lang/String;)I")},
|
||||
getVersionCodeId{environ->GetMethodID(instanceClass, "getVersionCode", "()I")},
|
||||
getIntegerValueId{environ->GetMethodID(environ->FindClass("java/lang/Integer"), "intValue", "()I")} {
|
||||
getIntegerValueId{environ->GetMethodID(environ->FindClass("java/lang/Integer"), "intValue", "()I")},
|
||||
getDhcpInfoId{environ->GetMethodID(instanceClass, "getDhcpInfo", "()Landroid/net/DhcpInfo;")} {
|
||||
env.Initialize(environ);
|
||||
}
|
||||
|
||||
|
@ -132,6 +133,23 @@ namespace skyline {
|
|||
return {static_cast<KeyboardCloseResult>(env->CallIntMethod(buttonInteger, getIntegerValueId)), input};
|
||||
}
|
||||
|
||||
DhcpInfo JvmManager::GetDhcpInfo() {
|
||||
jobject dhcpInfo{env->CallObjectMethod(instance, getDhcpInfoId)};
|
||||
jclass dhcpInfoClass{env->GetObjectClass(dhcpInfo)};
|
||||
jfieldID ipAddressFieldId{env->GetFieldID(dhcpInfoClass, "ipAddress", "I")};
|
||||
jfieldID subnetFieldId{env->GetFieldID(dhcpInfoClass, "netmask", "I")};
|
||||
jfieldID gatewayFieldId{env->GetFieldID(dhcpInfoClass, "gateway", "I")};
|
||||
jfieldID dns1FieldId{env->GetFieldID(dhcpInfoClass, "dns1", "I")};
|
||||
jfieldID dns2FieldId{env->GetFieldID(dhcpInfoClass, "dns2", "I")};
|
||||
|
||||
jint ipAddress{env->GetIntField(dhcpInfo, ipAddressFieldId)};
|
||||
jint subnet{env->GetIntField(dhcpInfo, subnetFieldId)};
|
||||
jint gateway{env->GetIntField(dhcpInfo, gatewayFieldId)};
|
||||
jint dns1{env->GetIntField(dhcpInfo, dns1FieldId)};
|
||||
jint dns2{env->GetIntField(dhcpInfo, dns2FieldId)};
|
||||
return DhcpInfo{ipAddress, subnet, gateway, dns1, dns2};
|
||||
}
|
||||
|
||||
void JvmManager::CloseKeyboard(jobject dialog) {
|
||||
env->CallVoidMethod(instance, closeKeyboardId, dialog);
|
||||
env->DeleteGlobalRef(dialog);
|
||||
|
|
|
@ -7,6 +7,14 @@
|
|||
#include <jni.h>
|
||||
|
||||
namespace skyline {
|
||||
struct DhcpInfo {
|
||||
i32 ipAddress;
|
||||
i32 subnet;
|
||||
i32 gateway;
|
||||
i32 dns1;
|
||||
i32 dns2;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A wrapper over std::string that supports construction using a JNI jstring
|
||||
*/
|
||||
|
@ -182,6 +190,11 @@ namespace skyline {
|
|||
*/
|
||||
i32 GetVersionCode();
|
||||
|
||||
/**
|
||||
* @brief A call to EmulationActivity.getDhcpInfo in Kotlin
|
||||
*/
|
||||
DhcpInfo GetDhcpInfo();
|
||||
|
||||
private:
|
||||
jmethodID initializeControllersId;
|
||||
jmethodID vibrateDeviceId;
|
||||
|
@ -191,6 +204,7 @@ namespace skyline {
|
|||
jmethodID closeKeyboardId;
|
||||
jmethodID showValidationResultId;
|
||||
jmethodID getVersionCodeId;
|
||||
jmethodID getDhcpInfoId;
|
||||
|
||||
jmethodID getIntegerValueId;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "IAsyncContext.h"
|
||||
|
||||
namespace skyline::service::account {
|
||||
IAsyncContext::IAsyncContext(const DeviceState &state, ServiceManager &manager) : BaseService{state, manager},
|
||||
systemEvent{std::make_shared<kernel::type::KEvent>(state, true)} {}
|
||||
|
||||
Result IAsyncContext::GetSystemEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto handle{state.process->InsertItem(systemEvent)};
|
||||
Logger::Debug("System Event Handle: 0x{:X}", handle);
|
||||
|
||||
response.copyHandles.push_back(handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IAsyncContext::Cancel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
systemEvent->Signal();
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IAsyncContext::HasDone(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push<u8>(1);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IAsyncContext::GetResult(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <services/serviceman.h>
|
||||
|
||||
namespace skyline::service::account {
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/Account_services#IAsyncContext
|
||||
*/
|
||||
class IAsyncContext : public BaseService {
|
||||
private:
|
||||
std::shared_ptr<kernel::type::KEvent> systemEvent;
|
||||
|
||||
public:
|
||||
IAsyncContext(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
Result GetSystemEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result Cancel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result HasDone(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetResult(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x0, IAsyncContext, GetSystemEvent),
|
||||
SFUNC(0x1, IAsyncContext, Cancel),
|
||||
SFUNC(0x2, IAsyncContext, HasDone),
|
||||
SFUNC(0x3, IAsyncContext, GetResult)
|
||||
)
|
||||
};
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "IAuthorizationRequest.h"
|
||||
#include "IAsyncContext.h"
|
||||
|
||||
namespace skyline::service::account {
|
||||
IAuthorizationRequest::IAuthorizationRequest(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
||||
|
||||
Result IAuthorizationRequest::InvokeWithoutInteractionAsync(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(SRVREG(IAsyncContext), session, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IAuthorizationRequest::IsAuthorized(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push<u8>(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IAuthorizationRequest::GetAuthorizationCode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <services/serviceman.h>
|
||||
|
||||
namespace skyline::service::account {
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/Account_services#IAuthorizationRequest
|
||||
*/
|
||||
class IAuthorizationRequest : public BaseService {
|
||||
public:
|
||||
IAuthorizationRequest(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
Result InvokeWithoutInteractionAsync(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result IsAuthorized(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetAuthorizationCode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0xA, IAuthorizationRequest, InvokeWithoutInteractionAsync),
|
||||
SFUNC(0x13, IAuthorizationRequest, IsAuthorized),
|
||||
SFUNC(0x15, IAuthorizationRequest, GetAuthorizationCode)
|
||||
)
|
||||
};
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "IManagerForApplication.h"
|
||||
#include "IAccountServiceForApplication.h"
|
||||
#include "IAsyncContext.h"
|
||||
#include "IAuthorizationRequest.h"
|
||||
|
||||
namespace skyline::service::account {
|
||||
IManagerForApplication::IManagerForApplication(const DeviceState &state, ServiceManager &manager, std::vector<UserId> &openedUsers) : BaseService(state, manager) {
|
||||
|
@ -19,6 +21,20 @@ namespace skyline::service::account {
|
|||
return {};
|
||||
}
|
||||
|
||||
Result IManagerForApplication::EnsureIdTokenCacheAsync(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(SRVREG(IAsyncContext), session, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IManagerForApplication::LoadIdTokenCache(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IManagerForApplication::CreateAuthorizationRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
manager.RegisterService(SRVREG(IAuthorizationRequest), session, response);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IManagerForApplication::StoreOpenContext(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
openedUsers->clear();
|
||||
openedUsers->push_back(constant::DefaultUserId);
|
||||
|
|
|
@ -28,11 +28,20 @@ namespace skyline::service::account {
|
|||
*/
|
||||
Result GetAccountId(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result EnsureIdTokenCacheAsync(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result LoadIdTokenCache(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result CreateAuthorizationRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result StoreOpenContext(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x0, IManagerForApplication, CheckAvailability),
|
||||
SFUNC(0x1, IManagerForApplication, GetAccountId),
|
||||
SFUNC(0x2, IManagerForApplication, EnsureIdTokenCacheAsync),
|
||||
SFUNC(0x3, IManagerForApplication, LoadIdTokenCache),
|
||||
SFUNC(0x96, IManagerForApplication, CreateAuthorizationRequest),
|
||||
SFUNC(0xA0, IManagerForApplication, StoreOpenContext)
|
||||
)
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "IUserLocalCommunicationService.h"
|
||||
#include <common/settings.h>
|
||||
|
||||
namespace skyline::service::ldn {
|
||||
IUserLocalCommunicationService::IUserLocalCommunicationService(const DeviceState &state, ServiceManager &manager)
|
||||
|
@ -9,8 +10,39 @@ namespace skyline::service::ldn {
|
|||
event{std::make_shared<type::KEvent>(state, false)} {}
|
||||
|
||||
Result IUserLocalCommunicationService::GetState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
static constexpr u32 StateNone{0x0};
|
||||
response.Push<u32>(StateNone);
|
||||
response.Push(State::Error);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::GetNetworkInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (request.outputBuf.at(0).size() != sizeof(NetworkInfo)) {
|
||||
Logger::Error("Invalid input");
|
||||
return result::InvalidInput;
|
||||
}
|
||||
|
||||
NetworkInfo networkInfo{};
|
||||
request.outputBuf.at(0).as<NetworkInfo>() = networkInfo;
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::GetIpv4Address(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::GetDisconnectReason(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push(DisconnectReason::None);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::GetSecurityParameter(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
SecurityParameter securityParameter{};
|
||||
response.Push(securityParameter);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::GetNetworkConfig(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
NetworkConfig networkConfig{};
|
||||
response.Push(networkConfig);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -21,15 +53,71 @@ namespace skyline::service::ldn {
|
|||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::GetNetworkInfoLatestUpdate(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
const size_t networkBuffferSize{request.outputBuf.at(0).size()};
|
||||
const size_t nodeBufferCount{request.outputBuf.at(1).size() / sizeof(NodeLatestUpdate)};
|
||||
|
||||
if (nodeBufferCount == 0 || networkBuffferSize != sizeof(NetworkInfo))
|
||||
return result::InvalidInput;
|
||||
|
||||
NetworkInfo networkInfo{};
|
||||
std::vector<NodeLatestUpdate> latestUpdate(nodeBufferCount);
|
||||
|
||||
request.outputBuf.at(0).as<NetworkInfo>() = networkInfo;
|
||||
request.outputBuf.at(1).copy_from(latestUpdate);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::Scan(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
const size_t networkInfoSize{request.outputBuf.at(0).size() / sizeof(NetworkInfo)};
|
||||
|
||||
if (networkInfoSize == 0)
|
||||
return result::InvalidInput;
|
||||
|
||||
std::vector<NetworkInfo> networkInfos(networkInfoSize);
|
||||
request.outputBuf.at(0).copy_from(networkInfos);
|
||||
response.Push<u32>(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::OpenAccessPoint(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::CreateNetwork(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::CreateNetworkPrivate(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::SetAdvertiseData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::OpenStation(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::InitializeSystem(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return result::DeviceDisabled;
|
||||
if (!*state.settings->isInternetEnabled)
|
||||
return result::AirplaneModeEnabled;
|
||||
|
||||
isInitialized = true;
|
||||
return result::AirplaneModeEnabled;
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::FinalizeSystem(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
isInitialized = false;
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IUserLocalCommunicationService::InitializeSystem2(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return result::DeviceDisabled;
|
||||
if (!*state.settings->isInternetEnabled)
|
||||
return result::AirplaneModeEnabled;
|
||||
|
||||
isInitialized = true;
|
||||
return result::AirplaneModeEnabled;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,194 @@
|
|||
|
||||
namespace skyline::service::ldn {
|
||||
namespace result {
|
||||
static constexpr Result DeviceDisabled{203, 22};
|
||||
constexpr Result AirplaneModeEnabled{203, 23};
|
||||
constexpr Result InvalidInput{203, 96};
|
||||
}
|
||||
|
||||
constexpr size_t SsidLengthMax = 32;
|
||||
constexpr size_t UserNameBytesMax = 32;
|
||||
constexpr i32 NodeCountMax = 8;
|
||||
constexpr size_t AdvertiseDataSizeMax = 384;
|
||||
constexpr size_t PassphraseLengthMax = 64;
|
||||
|
||||
enum class State : u32 {
|
||||
None,
|
||||
Initialized,
|
||||
AccessPointOpened,
|
||||
AccessPointCreated,
|
||||
StationOpened,
|
||||
StationConnected,
|
||||
Error,
|
||||
};
|
||||
|
||||
enum class DisconnectReason : i16 {
|
||||
Unknown = -1,
|
||||
None,
|
||||
User,
|
||||
System,
|
||||
DestroyedByUser,
|
||||
DestroyedBySystemRequest,
|
||||
Admin,
|
||||
SignalLost,
|
||||
};
|
||||
|
||||
enum class WifiChannel : i16 {
|
||||
Default = 0,
|
||||
Wifi24_1 = 1,
|
||||
Wifi24_6 = 6,
|
||||
Wifi24_11 = 11,
|
||||
Wifi50_36 = 36,
|
||||
Wifi50_40 = 40,
|
||||
Wifi50_44 = 44,
|
||||
Wifi50_48 = 48,
|
||||
};
|
||||
|
||||
enum class LinkLevel : i8 {
|
||||
Bad,
|
||||
Low,
|
||||
Good,
|
||||
Excellent,
|
||||
};
|
||||
|
||||
enum class PackedNetworkType : u8 {
|
||||
None,
|
||||
General,
|
||||
Ldn,
|
||||
All,
|
||||
};
|
||||
|
||||
enum class SecurityMode : u16 {
|
||||
All,
|
||||
Retail,
|
||||
Debug,
|
||||
};
|
||||
|
||||
enum class AcceptPolicy : u8 {
|
||||
AcceptAll,
|
||||
RejectAll,
|
||||
BlackList,
|
||||
WhiteList,
|
||||
};
|
||||
|
||||
enum class NodeStateChange : u8 {
|
||||
None,
|
||||
Connect,
|
||||
Disconnect,
|
||||
DisconnectAndConnect,
|
||||
};
|
||||
|
||||
struct IntentId {
|
||||
u64 localCommunicationId;
|
||||
u8 _pad0_[0x2];
|
||||
u16 sceneId;
|
||||
u8 _pad1_[0x4];
|
||||
};
|
||||
static_assert(sizeof(IntentId) == 0x10);
|
||||
|
||||
struct SessionId {
|
||||
u64 high;
|
||||
u64 low;
|
||||
};
|
||||
static_assert(sizeof(SessionId) == 0x10);
|
||||
|
||||
struct NetworkId {
|
||||
IntentId intentId;
|
||||
SessionId sessionId;
|
||||
};
|
||||
static_assert(sizeof(NetworkId) == 0x20);
|
||||
|
||||
struct MacAddress {
|
||||
std::array<u8, 6> raw{};
|
||||
};
|
||||
static_assert(sizeof(MacAddress) == 0x6);
|
||||
|
||||
struct Ssid {
|
||||
u8 length{};
|
||||
std::array<char, SsidLengthMax + 1> raw{};
|
||||
};
|
||||
static_assert(sizeof(Ssid) == 0x22);
|
||||
|
||||
struct CommonNetworkInfo {
|
||||
MacAddress bssid;
|
||||
Ssid ssid;
|
||||
WifiChannel channel;
|
||||
LinkLevel linkLevel;
|
||||
PackedNetworkType networkType;
|
||||
u8 _pad0_[0x4];
|
||||
};
|
||||
static_assert(sizeof(CommonNetworkInfo) == 0x30);
|
||||
|
||||
struct NodeInfo {
|
||||
std::array<u8, 4> ipv4Address;
|
||||
MacAddress macAddress;
|
||||
i8 nodeId;
|
||||
u8 isConnected;
|
||||
std::array<u8, UserNameBytesMax + 1> username;
|
||||
u8 _pad0_[0x1];
|
||||
i16 localCommunicationVersion;
|
||||
u8 _pad1_[0x10];
|
||||
};
|
||||
static_assert(sizeof(NodeInfo) == 0x40);
|
||||
|
||||
struct LdnNetworkInfo {
|
||||
std::array<u8, 0x10> securityParameter;
|
||||
SecurityMode securityMode;
|
||||
AcceptPolicy stationAcceptPolicy;
|
||||
u8 hasActionFrame;
|
||||
u8 _pad0_[0x2];
|
||||
u8 nodeCountMax;
|
||||
u8 nodeCount;
|
||||
std::array<NodeInfo, NodeCountMax> nodes;
|
||||
u8 _pad1_[0x2];
|
||||
u16 advertiseDataSize;
|
||||
std::array<u8, AdvertiseDataSizeMax> advertiseData;
|
||||
u8 _pad2_[0x8C];
|
||||
u64 randomAuthenticationId;
|
||||
};
|
||||
static_assert(sizeof(LdnNetworkInfo) == 0x430);
|
||||
|
||||
struct NetworkInfo {
|
||||
NetworkId networkId;
|
||||
CommonNetworkInfo common;
|
||||
LdnNetworkInfo ldn;
|
||||
};
|
||||
static_assert(sizeof(NetworkInfo) == 0x480);
|
||||
|
||||
struct SecurityConfig {
|
||||
SecurityMode securityMode;
|
||||
u16 passphraseSize;
|
||||
std::array<u8, PassphraseLengthMax> passphrase;
|
||||
};
|
||||
static_assert(sizeof(SecurityConfig) == 0x44);
|
||||
|
||||
struct SecurityParameter {
|
||||
std::array<u8, 0x10> data;
|
||||
SessionId sessionId;
|
||||
};
|
||||
static_assert(sizeof(SecurityParameter) == 0x20);
|
||||
|
||||
struct UserConfig {
|
||||
std::array<u8, UserNameBytesMax + 1> username;
|
||||
u8 _pad0_[0xF];
|
||||
};
|
||||
static_assert(sizeof(UserConfig) == 0x30);
|
||||
|
||||
struct NetworkConfig {
|
||||
IntentId intentId;
|
||||
WifiChannel channel;
|
||||
u8 nodeCountMax;
|
||||
u8 _pad0_[0x1];
|
||||
u16 localCommunicationVersion;
|
||||
u8 _pad1_[0xA];
|
||||
};
|
||||
static_assert(sizeof(NetworkConfig) == 0x20);
|
||||
|
||||
struct NodeLatestUpdate {
|
||||
NodeStateChange stateChange;
|
||||
u8 _pad0_[0x7];
|
||||
};
|
||||
static_assert(sizeof(NodeLatestUpdate) == 0x8);
|
||||
|
||||
/**
|
||||
* @brief IUserLocalCommunicationService is used by applications to manage LDN sessions
|
||||
* @url https://switchbrew.org/wiki/LDN_services#IUserLocalCommunicationService
|
||||
|
@ -17,6 +202,7 @@ namespace skyline::service::ldn {
|
|||
class IUserLocalCommunicationService : public BaseService {
|
||||
private:
|
||||
std::shared_ptr<type::KEvent> event; //!< The KEvent that is signalled on state changes
|
||||
bool isInitialized{false};
|
||||
|
||||
public:
|
||||
IUserLocalCommunicationService(const DeviceState &state, ServiceManager &manager);
|
||||
|
@ -26,11 +212,71 @@ namespace skyline::service::ldn {
|
|||
*/
|
||||
Result GetState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#GetNetworkInfo
|
||||
*/
|
||||
Result GetNetworkInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#GetIpv4Address
|
||||
*/
|
||||
Result GetIpv4Address(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#GetDisconnectReason
|
||||
*/
|
||||
Result GetDisconnectReason(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#GetSecurityParameter
|
||||
*/
|
||||
Result GetSecurityParameter(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#GetNetworkConfig
|
||||
*/
|
||||
Result GetNetworkConfig(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#AttachStateChangeEvent
|
||||
*/
|
||||
Result AttachStateChangeEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#GetNetworkInfoLatestUpdate
|
||||
*/
|
||||
Result GetNetworkInfoLatestUpdate(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#Scan
|
||||
*/
|
||||
Result Scan(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#OpenAccessPoint
|
||||
*/
|
||||
Result OpenAccessPoint(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#CreateNetwork
|
||||
*/
|
||||
Result CreateNetwork(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#CreateNetworkPrivate
|
||||
*/
|
||||
Result CreateNetworkPrivate(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#SetAdvertiseData
|
||||
*/
|
||||
Result SetAdvertiseData(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#OpenStation
|
||||
*/
|
||||
Result OpenStation(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/LDN_services#InitializeSystem
|
||||
*/
|
||||
|
@ -48,10 +294,22 @@ namespace skyline::service::ldn {
|
|||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x0, IUserLocalCommunicationService, GetState),
|
||||
SFUNC(0x1, IUserLocalCommunicationService, GetNetworkInfo),
|
||||
SFUNC(0x2, IUserLocalCommunicationService, GetIpv4Address),
|
||||
SFUNC(0x3, IUserLocalCommunicationService, GetDisconnectReason),
|
||||
SFUNC(0x4, IUserLocalCommunicationService, GetSecurityParameter),
|
||||
SFUNC(0x5, IUserLocalCommunicationService, GetNetworkConfig),
|
||||
SFUNC(0x64, IUserLocalCommunicationService, AttachStateChangeEvent),
|
||||
SFUNC(0x65, IUserLocalCommunicationService, GetNetworkInfoLatestUpdate),
|
||||
SFUNC(0x66, IUserLocalCommunicationService, Scan),
|
||||
SFUNC(0xC8, IUserLocalCommunicationService, OpenAccessPoint),
|
||||
SFUNC(0xCA, IUserLocalCommunicationService, CreateNetwork),
|
||||
SFUNC(0xCB, IUserLocalCommunicationService, CreateNetworkPrivate),
|
||||
SFUNC(0xCE, IUserLocalCommunicationService, SetAdvertiseData),
|
||||
SFUNC(0x12C, IUserLocalCommunicationService, OpenStation),
|
||||
SFUNC(0x190, IUserLocalCommunicationService, InitializeSystem),
|
||||
SFUNC(0x191, IUserLocalCommunicationService, FinalizeSystem),
|
||||
SFUNC(0x192, IUserLocalCommunicationService, InitializeSystem2),
|
||||
SFUNC(0x192, IUserLocalCommunicationService, InitializeSystem2)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,8 +4,22 @@
|
|||
#include "IScanRequest.h"
|
||||
#include "IRequest.h"
|
||||
#include "IGeneralService.h"
|
||||
#include <common/settings.h>
|
||||
#include <jvm.h>
|
||||
|
||||
namespace skyline::service::nifm {
|
||||
/**
|
||||
* @brief Converts integer value to an array of bytes ordered in little-endian format
|
||||
*/
|
||||
static std::array<u8, 4> ConvertIntToByteArray(i32 value) {
|
||||
std::array<u8, 4> result{};
|
||||
result[0] = value & 0xFF;
|
||||
result[1] = (value >> 8) & 0xFF;
|
||||
result[2] = (value >> 16) & 0xFF;
|
||||
result[3] = (value >> 24) & 0xFF;
|
||||
return result;
|
||||
}
|
||||
|
||||
IGeneralService::IGeneralService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
||||
|
||||
Result IGeneralService::CreateScanRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
|
@ -18,13 +32,89 @@ namespace skyline::service::nifm {
|
|||
return {};
|
||||
}
|
||||
|
||||
Result IGeneralService::GetCurrentNetworkProfile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!(*state.settings->isInternetEnabled))
|
||||
return result::NoInternetConnection;
|
||||
|
||||
const UUID uuid{static_cast<u128>(0xdeadbeef) << 64};
|
||||
auto dhcpInfo{state.jvm->GetDhcpInfo()};
|
||||
|
||||
SfNetworkProfileData networkProfileData{
|
||||
.ipSettingData{
|
||||
.ipAddressSetting{
|
||||
true,
|
||||
.currentAddress{ConvertIntToByteArray(dhcpInfo.ipAddress)},
|
||||
.subnetMask{ConvertIntToByteArray(dhcpInfo.subnet)},
|
||||
.gateway{ConvertIntToByteArray(dhcpInfo.gateway)},
|
||||
},
|
||||
.dnsSetting{
|
||||
true,
|
||||
.primaryDns{ConvertIntToByteArray(dhcpInfo.dns1)},
|
||||
.secondaryDns{ConvertIntToByteArray(dhcpInfo.dns2)},
|
||||
},
|
||||
.proxySetting{
|
||||
false,
|
||||
.port{},
|
||||
.proxyServer{},
|
||||
.automaticAuthEnabled{},
|
||||
.user{},
|
||||
.password{},
|
||||
},
|
||||
1500,
|
||||
},
|
||||
.uuid{uuid},
|
||||
.networkName{"Skyline Network"},
|
||||
.wirelessSettingData{
|
||||
12,
|
||||
.ssid{"Skyline Network"},
|
||||
.passphrase{"skylinepassword"},
|
||||
},
|
||||
};
|
||||
|
||||
request.outputBuf.at(0).as<SfNetworkProfileData>() = networkProfileData;
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IGeneralService::GetCurrentIpAddress(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return result::NoInternetConnection;
|
||||
if (!(*state.settings->isInternetEnabled))
|
||||
return result::NoInternetConnection;
|
||||
|
||||
auto dhcpInfo{state.jvm->GetDhcpInfo()};
|
||||
response.Push(ConvertIntToByteArray(dhcpInfo.ipAddress));
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IGeneralService::GetCurrentIpConfigInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!(*state.settings->isInternetEnabled))
|
||||
return result::NoInternetConnection;
|
||||
|
||||
auto dhcpInfo{state.jvm->GetDhcpInfo()};
|
||||
|
||||
struct IpConfigInfo {
|
||||
IpAddressSetting ipAddressSetting;
|
||||
DnsSetting dnsSetting;
|
||||
};
|
||||
|
||||
IpConfigInfo ipConfigInfo{
|
||||
.ipAddressSetting{
|
||||
true,
|
||||
.currentAddress{ConvertIntToByteArray(dhcpInfo.ipAddress)},
|
||||
.subnetMask{ConvertIntToByteArray(dhcpInfo.subnet)},
|
||||
.gateway{ConvertIntToByteArray(dhcpInfo.gateway)},
|
||||
},
|
||||
.dnsSetting{
|
||||
true,
|
||||
.primaryDns{ConvertIntToByteArray(dhcpInfo.dns1)},
|
||||
.secondaryDns{ConvertIntToByteArray(dhcpInfo.dns2)},
|
||||
},
|
||||
};
|
||||
|
||||
response.Push(ipConfigInfo);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IGeneralService::IsAnyInternetRequestAccepted(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
// We don't emulate networking so always return false
|
||||
response.Push(false);
|
||||
response.Push<u8>(*state.settings->isInternetEnabled);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,90 @@
|
|||
#pragma once
|
||||
|
||||
#include <services/serviceman.h>
|
||||
#include "common/uuid.h"
|
||||
|
||||
namespace skyline::service::nifm {
|
||||
namespace result {
|
||||
constexpr Result NoInternetConnection{110, 300};
|
||||
}
|
||||
|
||||
struct IpAddressSetting {
|
||||
bool isAutomatic{};
|
||||
std::array<u8, 4> currentAddress{};
|
||||
std::array<u8, 4> subnetMask{};
|
||||
std::array<u8, 4> gateway{};
|
||||
};
|
||||
static_assert(sizeof(IpAddressSetting) == 0xD);
|
||||
|
||||
struct DnsSetting {
|
||||
bool isAutomatic{};
|
||||
std::array<u8, 4> primaryDns{};
|
||||
std::array<u8, 4> secondaryDns{};
|
||||
};
|
||||
static_assert(sizeof(DnsSetting) == 0x9);
|
||||
|
||||
struct ProxySetting {
|
||||
bool enabled{};
|
||||
u8 _pad0_[0x1];
|
||||
u16 port{};
|
||||
std::array<char, 0x64> proxyServer{};
|
||||
bool automaticAuthEnabled{};
|
||||
std::array<char, 0x20> user{};
|
||||
std::array<char, 0x20> password{};
|
||||
u8 _pad1_[0x1];
|
||||
};
|
||||
static_assert(sizeof(ProxySetting) == 0xAA);
|
||||
|
||||
struct IpSettingData {
|
||||
IpAddressSetting ipAddressSetting{};
|
||||
DnsSetting dnsSetting{};
|
||||
ProxySetting proxySetting{};
|
||||
u16 mtu{};
|
||||
};
|
||||
static_assert(sizeof(IpSettingData) == 0xC2);
|
||||
|
||||
struct SfWirelessSettingData {
|
||||
u8 ssidLength{};
|
||||
std::array<char, 0x20> ssid{};
|
||||
u8 _unk0_[0x3];
|
||||
std::array<char, 0x41> passphrase{};
|
||||
};
|
||||
static_assert(sizeof(SfWirelessSettingData) == 0x65);
|
||||
|
||||
struct NifmWirelessSettingData {
|
||||
u8 ssidLength{};
|
||||
std::array<char, 0x21> ssid{};
|
||||
u8 _unk0_[0x1];
|
||||
u8 _pad0_[0x1];
|
||||
u32 _unk1_[0x2];
|
||||
std::array<char, 0x41> passphrase{};
|
||||
u8 _pad1_[0x3];
|
||||
};
|
||||
static_assert(sizeof(NifmWirelessSettingData) == 0x70);
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct SfNetworkProfileData {
|
||||
IpSettingData ipSettingData{};
|
||||
UUID uuid{};
|
||||
std::array<char, 0x40> networkName{};
|
||||
u8 _unk0_[0x4];
|
||||
SfWirelessSettingData wirelessSettingData{};
|
||||
u8 _pad0_[0x1];
|
||||
};
|
||||
static_assert(sizeof(SfNetworkProfileData) == 0x17C);
|
||||
|
||||
struct NifmNetworkProfileData {
|
||||
UUID uuid{};
|
||||
std::array<char, 0x40> networkName{};
|
||||
u32 _unk0_[0x2];
|
||||
u8 _unk1_[0x2];
|
||||
u8 _pad0_[0x2];
|
||||
NifmWirelessSettingData wirelessSettingData{};
|
||||
IpSettingData ipSettingData{};
|
||||
};
|
||||
static_assert(sizeof(NifmNetworkProfileData) == 0x18E);
|
||||
#pragma pack(pop)
|
||||
|
||||
/**
|
||||
* @brief IGeneralService is used by applications to control the network connection
|
||||
* @url https://switchbrew.org/wiki/Network_Interface_services#IGeneralService
|
||||
|
@ -27,11 +106,21 @@ namespace skyline::service::nifm {
|
|||
*/
|
||||
Result CreateRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/Network_Interface_services#GetCurrentNetworkProfile
|
||||
*/
|
||||
Result GetCurrentNetworkProfile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/Network_Interface_services#GetCurrentIpAddress
|
||||
*/
|
||||
Result GetCurrentIpAddress(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/Network_Interface_services#GetCurrentIpConfigInfo
|
||||
*/
|
||||
Result GetCurrentIpConfigInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/Network_Interface_services#IsAnyInternetRequestAccepted
|
||||
*/
|
||||
|
@ -40,7 +129,9 @@ namespace skyline::service::nifm {
|
|||
SERVICE_DECL(
|
||||
SFUNC(0x1, IGeneralService, CreateScanRequest),
|
||||
SFUNC(0x4, IGeneralService, CreateRequest),
|
||||
SFUNC(0x5, IGeneralService, GetCurrentNetworkProfile),
|
||||
SFUNC(0xC, IGeneralService, GetCurrentIpAddress),
|
||||
SFUNC(0xF, IGeneralService, GetCurrentIpConfigInfo),
|
||||
SFUNC(0x15, IGeneralService, IsAnyInternetRequestAccepted)
|
||||
)
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include "IRequest.h"
|
||||
#include <common/settings.h>
|
||||
|
||||
namespace skyline::service::nifm {
|
||||
namespace result {
|
||||
|
@ -15,8 +16,10 @@ namespace skyline::service::nifm {
|
|||
BaseService(state, manager) {}
|
||||
|
||||
Result IRequest::GetRequestState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
constexpr u32 Unsubmitted{1}; //!< The request has not been submitted
|
||||
response.Push<u32>(Unsubmitted);
|
||||
if (*state.settings->isInternetEnabled)
|
||||
response.Push(RequestState::Accepted);
|
||||
else
|
||||
response.Push(RequestState::Invalid);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -36,6 +39,10 @@ namespace skyline::service::nifm {
|
|||
return {};
|
||||
}
|
||||
|
||||
Result IRequest::Cancel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IRequest::Submit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -7,6 +7,17 @@
|
|||
#include <services/serviceman.h>
|
||||
|
||||
namespace skyline::service::nifm {
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/Network_Interface_services#RequestState
|
||||
*/
|
||||
enum class RequestState : u32 {
|
||||
Invalid = 0,
|
||||
Free = 1,
|
||||
OnHold = 2,
|
||||
Accepted = 3,
|
||||
Blocking = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief IRequest is used by applications to bring up a network
|
||||
* @url https://switchbrew.org/wiki/Network_Interface_services#IRequest
|
||||
|
@ -37,6 +48,11 @@ namespace skyline::service::nifm {
|
|||
*/
|
||||
Result GetSystemEventReadableHandles(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/Network_Interface_services#Cancel
|
||||
*/
|
||||
Result Cancel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Submits a request to bring up a network
|
||||
* @url https://switchbrew.org/wiki/Network_Interface_services#Submit
|
||||
|
@ -57,9 +73,10 @@ namespace skyline::service::nifm {
|
|||
SFUNC(0x0, IRequest, GetRequestState),
|
||||
SFUNC(0x1, IRequest, GetResult),
|
||||
SFUNC(0x2, IRequest, GetSystemEventReadableHandles),
|
||||
SFUNC(0x3, IRequest, Cancel),
|
||||
SFUNC(0x4, IRequest, Submit),
|
||||
SFUNC(0xB, IRequest, SetConnectionConfirmationOption),
|
||||
SFUNC(0x15, IRequest, GetAppletInfo)
|
||||
)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "IEnsureNetworkClockAvailabilityService.h"
|
||||
#include <common/settings.h>
|
||||
|
||||
namespace skyline::service::ntc {
|
||||
IEnsureNetworkClockAvailabilityService::IEnsureNetworkClockAvailabilityService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager),
|
||||
finishNotificationEvent(std::make_shared<kernel::type::KEvent>(state, false)) {}
|
||||
|
||||
Result IEnsureNetworkClockAvailabilityService::StartTask(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
if (!(*state.settings->isInternetEnabled))
|
||||
return result::NetworkTimeNotAvailable;
|
||||
finishNotificationEvent->Signal();
|
||||
return {};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <services/serviceman.h>
|
||||
|
||||
namespace skyline::service::ntc {
|
||||
namespace result {
|
||||
constexpr Result NetworkTimeNotAvailable{116, 1000};
|
||||
}
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/NIM_services#IEnsureNetworkClockAvailabilityService
|
||||
*/
|
||||
class IEnsureNetworkClockAvailabilityService : public BaseService {
|
||||
private:
|
||||
std::shared_ptr<kernel::type::KEvent> finishNotificationEvent;
|
||||
|
||||
public:
|
||||
IEnsureNetworkClockAvailabilityService(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
Result StartTask(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x0, IEnsureNetworkClockAvailabilityService, StartTask)
|
||||
)
|
||||
};
|
||||
}
|
|
@ -11,7 +11,10 @@ namespace skyline::service::pctl {
|
|||
}
|
||||
|
||||
Result IParentalControlService::CheckFreeCommunicationPermission(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push<u8>(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IParentalControlService::EndFreeCommunication(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,14 @@ namespace skyline::service::pctl {
|
|||
|
||||
Result CheckFreeCommunicationPermission(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result EndFreeCommunication(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result IsFreeCommunicationAvailable(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x1, IParentalControlService, Initialize),
|
||||
SFUNC(0x3E9, IParentalControlService, CheckFreeCommunicationPermission),
|
||||
SFUNC(0x3F9, IParentalControlService, EndFreeCommunication),
|
||||
SFUNC(0x3FA, IParentalControlService, IsFreeCommunicationAvailable)
|
||||
)
|
||||
};
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "ro/IRoInterface.h"
|
||||
#include "mii/IStaticService.h"
|
||||
#include "olsc/IOlscServiceForApplication.h"
|
||||
#include "ntc/IEnsureNetworkClockAvailabilityService.h"
|
||||
#include "serviceman.h"
|
||||
|
||||
#define SERVICE_CASE(class, name, ...) \
|
||||
|
@ -124,6 +125,7 @@ namespace skyline::service {
|
|||
SERVICE_CASE(nfp::IUserManager, "nfp:user")
|
||||
SERVICE_CASE(nifm::IStaticService, "nifm:u")
|
||||
SERVICE_CASE(socket::IClient, "bsd:u")
|
||||
SERVICE_CASE(socket::IClient, "bsd:s")
|
||||
SERVICE_CASE(socket::IManager, "nsd:u")
|
||||
SERVICE_CASE(socket::IManager, "nsd:a")
|
||||
SERVICE_CASE(socket::IResolver, "sfdnsres")
|
||||
|
@ -144,6 +146,7 @@ namespace skyline::service {
|
|||
SERVICE_CASE(mii::IStaticService, "mii:e")
|
||||
SERVICE_CASE(mii::IStaticService, "mii:u")
|
||||
SERVICE_CASE(olsc::IOlscServiceForApplication, "olsc:u")
|
||||
SERVICE_CASE(ntc::IEnsureNetworkClockAvailabilityService, "ntc")
|
||||
default:
|
||||
std::string_view nameString(span(reinterpret_cast<char *>(&name), sizeof(name)).as_string(true));
|
||||
throw std::out_of_range(fmt::format("CreateService called with an unknown service name: {}", nameString));
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <poll.h>
|
||||
#include "IClient.h"
|
||||
|
||||
namespace skyline::service::socket {
|
||||
IClient::IClient(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
||||
|
||||
Result IClient::RegisterClient(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push<u32>(0);
|
||||
response.Push<i32>(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -15,53 +16,185 @@ namespace skyline::service::socket {
|
|||
return {};
|
||||
}
|
||||
|
||||
Result IClient::Select(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
Result IClient::Socket(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
i32 domain{request.Pop<i32>()};
|
||||
i32 type{request.Pop<i32>()};
|
||||
i32 protocol{request.Pop<i32>()};
|
||||
i32 fd{::socket(domain, type, protocol)};
|
||||
Logger::Info("File Descriptor {} with Domain {}, Type {}, Protocol {}", fd, domain, type, protocol);
|
||||
if (fd == -1)
|
||||
Logger::Error("Error creating socket: {}", strerror(errno));
|
||||
return PushBsdResult(response, fd, 0);
|
||||
}
|
||||
|
||||
Result IClient::Poll(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fdsCount{request.Pop<i32>()};
|
||||
i32 timeout{request.Pop<i32>()};
|
||||
|
||||
if (fdsCount == 0)
|
||||
return PushBsdResult(response, -1, 0);
|
||||
|
||||
span outputBuf{request.outputBuf.at(0)};
|
||||
auto fds{span<pollfd>(reinterpret_cast<pollfd*>(outputBuf.data()), static_cast<u32>(fdsCount))};
|
||||
i32 result{poll(fds.data(), static_cast<u32>(fdsCount), static_cast<i32>(timeout))};
|
||||
return PushBsdResult(response, result, errno);
|
||||
}
|
||||
|
||||
Result IClient::Recv(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 flags{request.Pop<i32>()};
|
||||
if (fcntl(fd, F_GETFL) == -1)
|
||||
return PushBsdResult(response, -1, EBADF);
|
||||
|
||||
bool shouldBlockAfterOperation{false};
|
||||
if (!(fcntl(fd, F_GETFL) & O_NONBLOCK) && (flags & MSG_EOR)) {
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
shouldBlockAfterOperation = true;
|
||||
}
|
||||
|
||||
ssize_t result{recv(fd, request.outputBuf.at(0).data(), request.outputBuf.at(0).size(), flags)};
|
||||
|
||||
if (shouldBlockAfterOperation)
|
||||
fcntl(fd, F_SETFL, MSG_EOR);
|
||||
return PushBsdResultErrno(response, result);
|
||||
}
|
||||
|
||||
Result IClient::RecvFrom(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 flags{request.Pop<i32>()};
|
||||
if (fcntl(fd, F_GETFL) == -1)
|
||||
return PushBsdResult(response, -1, EBADF);
|
||||
|
||||
bool shouldBlockAfterOperation{false};
|
||||
if (!(fcntl(fd, F_GETFL) & O_NONBLOCK) && (flags & MSG_EOR)) {
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
shouldBlockAfterOperation = true;
|
||||
}
|
||||
|
||||
sockaddr addrIn{};
|
||||
socklen_t addrLen{sizeof(addrIn)};
|
||||
span message{request.outputBuf.at(0)};
|
||||
ssize_t result{recvfrom(fd, message.data(), message.size(), 0, &addrIn, &addrLen)};
|
||||
|
||||
if (shouldBlockAfterOperation)
|
||||
fcntl(fd, F_SETFL, MSG_EOR);
|
||||
|
||||
request.outputBuf.at(0).copy_from(message);
|
||||
if (!request.outputBuf.at(1).empty())
|
||||
request.outputBuf.at(1).copy_from(span{addrIn});
|
||||
response.Push(request.outputBuf.at(1).size());
|
||||
return PushBsdResultErrno(response, result);
|
||||
}
|
||||
|
||||
Result IClient::Send(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 flags{request.Pop<i32>()};
|
||||
|
||||
ssize_t result{send(fd, request.inputBuf.at(0).data(), request.inputBuf.at(0).size(), flags)};
|
||||
return PushBsdResultErrno(response, result);
|
||||
}
|
||||
|
||||
Result IClient::SendTo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push<u32>(0);
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 flags{request.Pop<i32>()};
|
||||
|
||||
sockaddr addrIn{request.inputBuf.at(1).as<sockaddr>()};
|
||||
addrIn.sa_family = AF_INET;
|
||||
ssize_t result{sendto(fd, request.inputBuf.at(0).data(), request.inputBuf.at(0).size(), flags,
|
||||
&addrIn, sizeof(addrIn))};
|
||||
return PushBsdResultErrno(response, result);
|
||||
}
|
||||
|
||||
Result IClient::Accept(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
sockaddr addr{};
|
||||
socklen_t addrLen{sizeof(addr)};
|
||||
i32 result{accept(fd, &addr, &addrLen)};
|
||||
if (errno != 0)
|
||||
return PushBsdResult(response, -1, errno);
|
||||
|
||||
request.outputBuf.at(0).copy_from(span{addr});
|
||||
response.Push(request.outputBuf.at(0).size());
|
||||
return PushBsdResult(response, result, errno);
|
||||
}
|
||||
|
||||
Result IClient::Bind(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
sockaddr addr{request.inputBuf.at(0).as<sockaddr>()};
|
||||
addr.sa_family = AF_INET;
|
||||
|
||||
i32 result{bind(fd, &addr, sizeof(addr))};
|
||||
return PushBsdResult(response, 0, errno);
|
||||
}
|
||||
|
||||
Result IClient::Connect(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
sockaddr addr{request.inputBuf.at(0).as<sockaddr>()};
|
||||
addr.sa_family = AF_INET;
|
||||
|
||||
i32 result{connect(fd, &addr, sizeof(addr))};
|
||||
return PushBsdResult(response, 0, errno);
|
||||
}
|
||||
|
||||
Result IClient::GetPeerName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
i32 fd{request.Pop<i32>()};
|
||||
sockaddr addr{};
|
||||
socklen_t addrLen{sizeof(addr)};
|
||||
i32 result{getpeername(fd, &addr, &addrLen)};
|
||||
request.outputBuf.at(0).copy_from(span{addr});
|
||||
response.Push(request.outputBuf.at(0).size());
|
||||
return PushBsdResult(response, 0, errno);
|
||||
}
|
||||
|
||||
Result IClient::GetSockName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
i32 fd{request.Pop<i32>()};
|
||||
sockaddr addr{};
|
||||
socklen_t addrLen{sizeof(addr)};
|
||||
i32 result{getsockname(fd, &addr, &addrLen)};
|
||||
request.outputBuf.at(0).copy_from(span{addr});
|
||||
response.Push(request.outputBuf.at(0).size());
|
||||
return PushBsdResult(response, 0, errno);
|
||||
}
|
||||
|
||||
Result IClient::GetSockOpt(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 level{request.Pop<i32>()};
|
||||
OptionName optionName{request.Pop<OptionName>()};
|
||||
socklen_t addrLen{sizeof(request.outputBuf.at(0))};
|
||||
i32 result{getsockopt(fd, level, GetOption(optionName), request.outputBuf.at(0).data(), &addrLen)};
|
||||
return PushBsdResult(response, 0, errno);
|
||||
}
|
||||
|
||||
Result IClient::Listen(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 backlog{request.Pop<i32>()};
|
||||
i32 result{listen(fd, backlog)};
|
||||
return PushBsdResult(response, 0, errno);
|
||||
}
|
||||
|
||||
Result IClient::Fcntl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 cmd{request.Pop<i32>()};
|
||||
i32 arg{request.Pop<i32>()};
|
||||
i32 result{fcntl(fd, cmd, arg)};
|
||||
return PushBsdResult(response, 0, errno);
|
||||
}
|
||||
|
||||
Result IClient::SetSockOpt(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 level{request.Pop<i32>()};
|
||||
OptionName optionName{request.Pop<OptionName>()};
|
||||
if (level == 0xFFFF)
|
||||
level = SOL_SOCKET;
|
||||
i32 result{setsockopt(fd, level, GetOption(optionName), request.inputBuf.at(0).data(), static_cast<socklen_t>(request.inputBuf.at(0).size()))};
|
||||
return PushBsdResult(response, 0, errno);
|
||||
}
|
||||
|
||||
Result IClient::Shutdown(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 how{request.Pop<i32>()};
|
||||
i32 result{shutdown(fd, how)};
|
||||
return PushBsdResult(response, 0, errno);
|
||||
}
|
||||
|
||||
Result IClient::ShutdownAllSockets(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
|
@ -69,16 +202,52 @@ namespace skyline::service::socket {
|
|||
}
|
||||
|
||||
Result IClient::Write(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 flags{request.Pop<i32>()};
|
||||
ssize_t result{send(fd, request.inputBuf.at(0).data(), request.inputBuf.at(0).size(), flags)};
|
||||
return PushBsdResultErrno(response, result);
|
||||
}
|
||||
|
||||
Result IClient::Read(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
response.Push<u32>(0);
|
||||
response.Push<u32>(0);
|
||||
return {};
|
||||
i32 fd{request.Pop<i32>()};
|
||||
ssize_t result{recv(fd, request.outputBuf.at(0).data(), request.outputBuf.at(0).size(), 0)};
|
||||
return PushBsdResultErrno(response, result);
|
||||
}
|
||||
|
||||
Result IClient::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
i32 fd{request.Pop<i32>()};
|
||||
i32 result{close(fd)};
|
||||
return PushBsdResult(response, 0, errno);
|
||||
}
|
||||
|
||||
Result IClient::EventFd(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return PushBsdResult(response, 1, 0);
|
||||
}
|
||||
|
||||
Result IClient::PushBsdResult(ipc::IpcResponse &response, i32 result, i32 errorCode) {
|
||||
if (errorCode != 0)
|
||||
result = -1;
|
||||
|
||||
response.Push(result);
|
||||
response.Push(errorCode);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IClient::PushBsdResultErrno(ipc::IpcResponse &response, i64 result) {
|
||||
response.Push(result);
|
||||
response.Push(result == -1 ? errno : 0);
|
||||
return {};
|
||||
}
|
||||
|
||||
i32 IClient::GetOption(OptionName optionName) {
|
||||
switch (optionName) {
|
||||
case OptionName::ReuseAddr: return SO_REUSEADDR;
|
||||
case OptionName::Broadcast: return SO_BROADCAST;
|
||||
case OptionName::Linger: return SO_LINGER;
|
||||
case OptionName::SndBuf: return SO_SNDBUF;
|
||||
case OptionName::RcvBuf: return SO_RCVBUF;
|
||||
case OptionName::SndTimeo: return SO_SNDTIMEO;
|
||||
case OptionName::RcvTimeo: return SO_RCVTIMEO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <services/serviceman.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
namespace skyline::service::socket {
|
||||
enum class OptionName : u32 {
|
||||
ReuseAddr = 0x4,
|
||||
Broadcast = 0x20,
|
||||
Linger = 0x80,
|
||||
SndBuf = 0x1001,
|
||||
RcvBuf = 0x1002,
|
||||
SndTimeo = 0x1005,
|
||||
RcvTimeo = 0x1006,
|
||||
};
|
||||
/**
|
||||
* @brief IClient or bsd:u is used by applications create network sockets
|
||||
* @url https://switchbrew.org/wiki/Sockets_services#bsd:u.2C_bsd:s
|
||||
|
@ -25,10 +35,7 @@ namespace skyline::service::socket {
|
|||
*/
|
||||
Result StartMonitoring(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Selects the socket
|
||||
*/
|
||||
Result Select(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
Result Socket(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Polls the socket for events
|
||||
|
@ -70,11 +77,32 @@ namespace skyline::service::socket {
|
|||
*/
|
||||
Result Connect(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the address of the peer to which a socket is connected
|
||||
*/
|
||||
Result GetPeerName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the current local address of the socket
|
||||
*/
|
||||
Result GetSockName(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Retrieves socket options
|
||||
*/
|
||||
Result GetSockOpt(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Places a socket in a state in which it is listening for an incoming connection
|
||||
*/
|
||||
Result Listen(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Performs a control operation on an open file descriptor
|
||||
* @url https://switchbrew.org/wiki/Sockets_services#Fcntl
|
||||
*/
|
||||
Result Fcntl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief Manipulates the options associated with a socket
|
||||
*/
|
||||
|
@ -105,10 +133,21 @@ namespace skyline::service::socket {
|
|||
*/
|
||||
Result Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result EventFd(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result PushBsdResult(ipc::IpcResponse &response, i32 result, i32 errorCode);
|
||||
|
||||
Result PushBsdResultErrno(ipc::IpcResponse &response, i64 result);
|
||||
|
||||
/**
|
||||
* @brief Translates option name to a socket option
|
||||
*/
|
||||
i32 GetOption(OptionName optionName);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x0, IClient, RegisterClient),
|
||||
SFUNC(0x1, IClient, StartMonitoring),
|
||||
SFUNC(0x5, IClient, Select),
|
||||
SFUNC(0x2, IClient, Socket),
|
||||
SFUNC(0x6, IClient, Poll),
|
||||
SFUNC(0x8, IClient, Recv),
|
||||
SFUNC(0x9, IClient, RecvFrom),
|
||||
|
@ -117,13 +156,18 @@ namespace skyline::service::socket {
|
|||
SFUNC(0xC, IClient, Accept),
|
||||
SFUNC(0xD, IClient, Bind),
|
||||
SFUNC(0xE, IClient, Connect),
|
||||
SFUNC(0xF, IClient, GetPeerName),
|
||||
SFUNC(0x10, IClient, GetSockName),
|
||||
SFUNC(0x11, IClient, GetSockOpt),
|
||||
SFUNC(0x12, IClient, Listen),
|
||||
SFUNC(0x14, IClient, Fcntl),
|
||||
SFUNC(0x15, IClient, SetSockOpt),
|
||||
SFUNC(0x16, IClient, Shutdown),
|
||||
SFUNC(0x17, IClient, ShutdownAllSockets),
|
||||
SFUNC(0x18, IClient, Write),
|
||||
SFUNC(0x19, IClient, Read),
|
||||
SFUNC(0x1A, IClient, Close)
|
||||
SFUNC(0x1A, IClient, Close),
|
||||
SFUNC(0x1F, IClient, EventFd)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,4 +5,8 @@
|
|||
|
||||
namespace skyline::service::socket {
|
||||
IManager::IManager(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
||||
|
||||
Result IManager::ResolveEx(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,5 +12,11 @@ namespace skyline::service::socket {
|
|||
class IManager : public BaseService {
|
||||
public:
|
||||
IManager(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
Result ResolveEx(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x15, IManager, ResolveEx),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,7 +2,176 @@
|
|||
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include "IResolver.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <common/settings.h>
|
||||
|
||||
namespace skyline::service::socket {
|
||||
static NetDbError AddrInfoErrorToNetDbError(i32 result) {
|
||||
switch (result) {
|
||||
case 0:
|
||||
return NetDbError::Success;
|
||||
case EAI_AGAIN:
|
||||
return NetDbError::TryAgain;
|
||||
case EAI_NODATA:
|
||||
return NetDbError::NoData;
|
||||
default:
|
||||
return NetDbError::HostNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
IResolver::IResolver(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
|
||||
|
||||
Result IResolver::GetAddrInfoRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto [dataSize, resultCode]{GetAddrInfoRequestImpl(request)};
|
||||
response.Push<i32>(resultCode); // errno
|
||||
response.Push(AddrInfoErrorToNetDbError(resultCode)); // NetDBErrorCode
|
||||
response.Push<u32>(dataSize);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IResolver::GetHostByNameRequestWithOptions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IResolver::GetAddrInfoRequestWithOptions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
auto [dataSize, resultCode]{GetAddrInfoRequestImpl(request)};
|
||||
response.Push<i32>(resultCode); // errno
|
||||
response.Push(AddrInfoErrorToNetDbError(resultCode)); // NetDBErrorCode
|
||||
response.Push<u32>(dataSize);
|
||||
response.Push<u32>(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result IResolver::GetNameInfoRequestWithOptions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::pair<u32, i32> IResolver::GetAddrInfoRequestImpl(ipc::IpcRequest &request) {
|
||||
auto hostname{request.inputBuf.at(0).as_string(true)};
|
||||
auto service{request.inputBuf.at(1).as_string(true)};
|
||||
|
||||
if (!(*state.settings->isInternetEnabled)) {
|
||||
Logger::Info("Internet access disabled, DNS Blocked: {}", hostname);
|
||||
return {0, -1};
|
||||
}
|
||||
|
||||
addrinfo *result;
|
||||
i32 resultCode = getaddrinfo(hostname.data(), service.data(), nullptr, &result);
|
||||
|
||||
u32 dataSize{0};
|
||||
if (resultCode == 0 && result != nullptr) {
|
||||
const std::vector<u8> data = SerializeAddrInfo(result, resultCode, hostname);
|
||||
dataSize = static_cast<u32>(data.size());
|
||||
request.outputBuf.at(0).copy_from(data);
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
return {dataSize, resultCode};
|
||||
}
|
||||
|
||||
// https://github.com/yuzu-emu/yuzu/blob/ce8f4da63834be0179d98a7720dee47d65f3ec06/src/core/hle/service/sockets/sfdnsres.cpp#L76
|
||||
std::vector<u8> IResolver::SerializeAddrInfo(const addrinfo* addrinfo, i32 result_code, std::string_view host) {
|
||||
std::vector<u8> data;
|
||||
|
||||
auto* current{addrinfo};
|
||||
while (current != nullptr) {
|
||||
struct SerializedResponseHeader {
|
||||
u32 magic;
|
||||
u32 flags;
|
||||
u32 family;
|
||||
u32 socketType;
|
||||
u32 protocol;
|
||||
u32 addressLength;
|
||||
};
|
||||
static_assert(sizeof(SerializedResponseHeader) == 0x18);
|
||||
|
||||
constexpr auto headerSize{sizeof(SerializedResponseHeader)};
|
||||
const auto addrSize{current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4};
|
||||
const auto canonnameSize{current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1};
|
||||
|
||||
const auto lastSize{data.size()};
|
||||
data.resize(lastSize + headerSize + addrSize + canonnameSize);
|
||||
|
||||
// Header in network byte order
|
||||
SerializedResponseHeader header{};
|
||||
|
||||
constexpr auto HEADER_MAGIC{0xBEEFCAFE};
|
||||
header.magic = htonl(HEADER_MAGIC);
|
||||
header.family = htonl(current->ai_family);
|
||||
header.flags = htonl(current->ai_flags);
|
||||
header.socketType = htonl(current->ai_socktype);
|
||||
header.protocol = htonl(current->ai_protocol);
|
||||
header.addressLength = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0;
|
||||
|
||||
auto* headerPtr{data.data() + lastSize};
|
||||
std::memcpy(headerPtr, &header, headerSize);
|
||||
|
||||
if (header.addressLength == 0) {
|
||||
std::memset(headerPtr + headerSize, 0, 4);
|
||||
} else {
|
||||
switch (current->ai_family) {
|
||||
case AF_INET: {
|
||||
struct SockAddrIn {
|
||||
u16 sin_family;
|
||||
u16 sin_port;
|
||||
u32 sin_addr;
|
||||
u8 sin_zero[8];
|
||||
};
|
||||
|
||||
SockAddrIn serializedAddr{};
|
||||
const auto addr{*reinterpret_cast<sockaddr_in*>(current->ai_addr)};
|
||||
serializedAddr.sin_port = htons(addr.sin_port);
|
||||
serializedAddr.sin_family = htons(addr.sin_family);
|
||||
serializedAddr.sin_addr = htonl(addr.sin_addr.s_addr);
|
||||
std::memcpy(headerPtr + headerSize, &serializedAddr, sizeof(SockAddrIn));
|
||||
|
||||
char addrStringBuf[64]{};
|
||||
inet_ntop(AF_INET, &addr.sin_addr, addrStringBuf, std::size(addrStringBuf));
|
||||
Logger::Info("Resolved host '{}' to IPv4 address {}", host, addrStringBuf);
|
||||
break;
|
||||
}
|
||||
case AF_INET6: {
|
||||
struct SockAddrIn6 {
|
||||
u16 sin6_family;
|
||||
u16 sin6_port;
|
||||
u32 sin6_flowinfo;
|
||||
u8 sin6_addr[16];
|
||||
u32 sin6_scope_id;
|
||||
};
|
||||
|
||||
SockAddrIn6 serializedAddr{};
|
||||
const auto addr{*reinterpret_cast<sockaddr_in6*>(current->ai_addr)};
|
||||
serializedAddr.sin6_family = htons(addr.sin6_family);
|
||||
serializedAddr.sin6_port = htons(addr.sin6_port);
|
||||
serializedAddr.sin6_flowinfo = htonl(addr.sin6_flowinfo);
|
||||
serializedAddr.sin6_scope_id = htonl(addr.sin6_scope_id);
|
||||
std::memcpy(serializedAddr.sin6_addr, &addr.sin6_addr, sizeof(SockAddrIn6::sin6_addr));
|
||||
std::memcpy(headerPtr + headerSize, &serializedAddr, sizeof(SockAddrIn6));
|
||||
|
||||
char addrStringBuf[64]{};
|
||||
inet_ntop(AF_INET6, &addr.sin6_addr, addrStringBuf, std::size(addrStringBuf));
|
||||
Logger::Info("Resolved host '{}' to IPv6 address {}", host, addrStringBuf);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
std::memcpy(headerPtr + headerSize, current->ai_addr, addrSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (current->ai_canonname) {
|
||||
std::memcpy(headerPtr + addrSize, current->ai_canonname, canonnameSize);
|
||||
} else {
|
||||
*(headerPtr + headerSize + addrSize) = 0;
|
||||
}
|
||||
|
||||
current = current->ai_next;
|
||||
}
|
||||
|
||||
// 4-byte sentinel value
|
||||
data.push_back(0);
|
||||
data.push_back(0);
|
||||
data.push_back(0);
|
||||
data.push_back(0);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,13 +4,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <services/serviceman.h>
|
||||
#include <netdb.h>
|
||||
|
||||
namespace skyline::service::socket {
|
||||
enum class NetDbError : i32 {
|
||||
Internal = -1,
|
||||
Success = 0,
|
||||
HostNotFound = 1,
|
||||
TryAgain = 2,
|
||||
NoRecovery = 3,
|
||||
NoData = 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/Sockets_services#sfdnsres
|
||||
*/
|
||||
class IResolver : public BaseService {
|
||||
public:
|
||||
IResolver(const DeviceState &state, ServiceManager &manager);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/Sockets_services#GetAddrInfoRequest
|
||||
*/
|
||||
Result GetAddrInfoRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetHostByNameRequestWithOptions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetAddrInfoRequestWithOptions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
Result GetNameInfoRequestWithOptions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
std::pair<u32, i32> GetAddrInfoRequestImpl(ipc::IpcRequest &request);
|
||||
|
||||
std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, i32 result_code, std::string_view host);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x6, IResolver, GetAddrInfoRequest),
|
||||
SFUNC(0xA, IResolver, GetHostByNameRequestWithOptions),
|
||||
SFUNC(0xC, IResolver, GetAddrInfoRequestWithOptions),
|
||||
SFUNC(0xD, IResolver, GetNameInfoRequestWithOptions)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,4 +17,8 @@ namespace skyline::service::ssl {
|
|||
response.Push<u64>(0);
|
||||
return {};
|
||||
}
|
||||
|
||||
Result ISslContext::RegisterInternalPki(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,14 @@ namespace skyline::service::ssl {
|
|||
*/
|
||||
Result ImportServerPki(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @url https://switchbrew.org/wiki/SSL_services#RegisterInternalPki
|
||||
*/
|
||||
Result RegisterInternalPki(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
SERVICE_DECL(
|
||||
SFUNC(0x4, ISslContext, ImportServerPki)
|
||||
SFUNC(0x4, ISslContext, ImportServerPki),
|
||||
SFUNC(0x8, ISslContext, RegisterInternalPki)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ import android.content.res.Configuration
|
|||
import android.graphics.PointF
|
||||
import android.graphics.drawable.Icon
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.net.DhcpInfo
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import android.util.Rational
|
||||
|
@ -627,6 +629,12 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
|||
return dialog.waitForSubmitOrCancel().let { arrayOf(if (it.cancelled) 1 else 0, it.text) }
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun getDhcpInfo() : DhcpInfo {
|
||||
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
||||
return wifiManager.dhcpInfo
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun closeKeyboard(dialog : SoftwareKeyboardDialog) {
|
||||
runOnUiThread { dialog.dismiss() }
|
||||
|
|
|
@ -29,6 +29,7 @@ class EmulationSettings private constructor(context : Context, prefName : String
|
|||
var profilePictureValue by sharedPreferences(context, "", prefName = prefName)
|
||||
var systemLanguage by sharedPreferences(context, 1, prefName = prefName)
|
||||
var systemRegion by sharedPreferences(context, -1, prefName = prefName)
|
||||
var isInternetEnabled by sharedPreferences(context, false, prefName = prefName)
|
||||
|
||||
// Audio
|
||||
var isAudioOutputDisabled by sharedPreferences(context, false, prefName = prefName)
|
||||
|
|
|
@ -22,6 +22,7 @@ data class NativeSettings(
|
|||
var profilePictureValue : String,
|
||||
var systemLanguage : Int,
|
||||
var systemRegion : Int,
|
||||
var isInternetEnabled : Boolean,
|
||||
|
||||
// Audio
|
||||
var isAudioOutputDisabled : Boolean,
|
||||
|
@ -52,6 +53,7 @@ data class NativeSettings(
|
|||
pref.profilePictureValue,
|
||||
pref.systemLanguage,
|
||||
pref.systemRegion,
|
||||
pref.isInternetEnabled,
|
||||
pref.isAudioOutputDisabled,
|
||||
if (pref.gpuDriver == EmulationSettings.SYSTEM_GPU_DRIVER) "" else pref.gpuDriver,
|
||||
if (pref.gpuDriver == EmulationSettings.SYSTEM_GPU_DRIVER) "" else GpuDriverHelper.getLibraryName(context, pref.gpuDriver),
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
<string name="profile_picture">Profile Picture</string>
|
||||
<string name="system_language">System Language</string>
|
||||
<string name="system_region">System Region</string>
|
||||
<string name="internet">The system will be able to use internet</string>
|
||||
<!-- Settings - Display -->
|
||||
<string name="display">Display</string>
|
||||
<string name="perf_stats">Show Performance Statistics</string>
|
||||
|
|
|
@ -32,6 +32,11 @@
|
|||
app:key="system_region"
|
||||
app:title="@string/system_region"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:summary="@string/internet"
|
||||
app:key="is_internet_enabled"
|
||||
app:title="Enable Internet" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:key="category_presentation"
|
||||
|
|
Loading…
Reference in New Issue