This commit is contained in:
Dubrova Dzmitry 2023-05-13 15:28:10 +01:00 committed by GitHub
commit 82afbc604a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1276 additions and 40 deletions

View File

@ -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

View File

@ -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

View File

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

View File

@ -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

View File

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

View File

@ -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;
};

View File

@ -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 {};
}
}

View File

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

View File

@ -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 {};
}
}

View File

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

View File

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

View File

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

View File

@ -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;
}
}

View File

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

View File

@ -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 {};
}
}

View File

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

View File

@ -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 {};
}

View File

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

View File

@ -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 {};
}
}

View File

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

View File

@ -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 {};
}

View File

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

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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 {};
}
}

View File

@ -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),
)
};
}

View File

@ -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;
}
}

View File

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

View File

@ -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 {};
}
}

View File

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

View File

@ -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() }

View File

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

View File

@ -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),

View File

@ -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>

View File

@ -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"