diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index 03a0209d..949c4792 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -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
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9bf670ce..5ad78d78 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
+
("systemLanguage");
systemRegion = ktSettings.GetInt("systemRegion");
+ isInternetEnabled = ktSettings.GetBool("isInternetEnabled");
forceTripleBuffering = ktSettings.GetBool("forceTripleBuffering");
disableFrameThrottling = ktSettings.GetBool("disableFrameThrottling");
gpuDriver = ktSettings.GetString("gpuDriver");
diff --git a/app/src/main/cpp/skyline/common/settings.h b/app/src/main/cpp/skyline/common/settings.h
index e14c5834..40336a73 100644
--- a/app/src/main/cpp/skyline/common/settings.h
+++ b/app/src/main/cpp/skyline/common/settings.h
@@ -64,6 +64,7 @@ namespace skyline {
Setting profilePictureValue; //!< The profile picture path to be supplied to the guest
Setting systemLanguage; //!< The system language
Setting systemRegion; //!< The system region
+ Setting isInternetEnabled; //!< If emulator uses internet
// Display
Setting forceTripleBuffering; //!< If the presentation engine should always triple buffer even if the swapchain supports double buffering
diff --git a/app/src/main/cpp/skyline/jvm.cpp b/app/src/main/cpp/skyline/jvm.cpp
index 5af9c305..c36632c4 100644
--- a/app/src/main/cpp/skyline/jvm.cpp
+++ b/app/src/main/cpp/skyline/jvm.cpp
@@ -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(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);
diff --git a/app/src/main/cpp/skyline/jvm.h b/app/src/main/cpp/skyline/jvm.h
index 535a307a..6f593a2b 100644
--- a/app/src/main/cpp/skyline/jvm.h
+++ b/app/src/main/cpp/skyline/jvm.h
@@ -7,6 +7,14 @@
#include
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;
};
diff --git a/app/src/main/cpp/skyline/services/account/IAsyncContext.cpp b/app/src/main/cpp/skyline/services/account/IAsyncContext.cpp
new file mode 100644
index 00000000..58d084e2
--- /dev/null
+++ b/app/src/main/cpp/skyline/services/account/IAsyncContext.cpp
@@ -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(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(1);
+ return {};
+ }
+
+ Result IAsyncContext::GetResult(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
+ return {};
+ }
+}
diff --git a/app/src/main/cpp/skyline/services/account/IAsyncContext.h b/app/src/main/cpp/skyline/services/account/IAsyncContext.h
new file mode 100644
index 00000000..318f0bcd
--- /dev/null
+++ b/app/src/main/cpp/skyline/services/account/IAsyncContext.h
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
+
+#pragma once
+
+#include
+
+namespace skyline::service::account {
+ /**
+ * @url https://switchbrew.org/wiki/Account_services#IAsyncContext
+ */
+ class IAsyncContext : public BaseService {
+ private:
+ std::shared_ptr 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)
+ )
+ };
+}
diff --git a/app/src/main/cpp/skyline/services/account/IAuthorizationRequest.cpp b/app/src/main/cpp/skyline/services/account/IAuthorizationRequest.cpp
new file mode 100644
index 00000000..d64e6709
--- /dev/null
+++ b/app/src/main/cpp/skyline/services/account/IAuthorizationRequest.cpp
@@ -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(0);
+ return {};
+ }
+
+ Result IAuthorizationRequest::GetAuthorizationCode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
+ return {};
+ }
+}
diff --git a/app/src/main/cpp/skyline/services/account/IAuthorizationRequest.h b/app/src/main/cpp/skyline/services/account/IAuthorizationRequest.h
new file mode 100644
index 00000000..38863765
--- /dev/null
+++ b/app/src/main/cpp/skyline/services/account/IAuthorizationRequest.h
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
+
+#pragma once
+
+#include
+
+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)
+ )
+ };
+}
diff --git a/app/src/main/cpp/skyline/services/account/IManagerForApplication.cpp b/app/src/main/cpp/skyline/services/account/IManagerForApplication.cpp
index 460d0ca4..52a2971f 100644
--- a/app/src/main/cpp/skyline/services/account/IManagerForApplication.cpp
+++ b/app/src/main/cpp/skyline/services/account/IManagerForApplication.cpp
@@ -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 &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);
diff --git a/app/src/main/cpp/skyline/services/account/IManagerForApplication.h b/app/src/main/cpp/skyline/services/account/IManagerForApplication.h
index 2e28d134..7832d573 100644
--- a/app/src/main/cpp/skyline/services/account/IManagerForApplication.h
+++ b/app/src/main/cpp/skyline/services/account/IManagerForApplication.h
@@ -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)
)
};
diff --git a/app/src/main/cpp/skyline/services/ldn/IUserLocalCommunicationService.cpp b/app/src/main/cpp/skyline/services/ldn/IUserLocalCommunicationService.cpp
index 4b118c69..2770460a 100644
--- a/app/src/main/cpp/skyline/services/ldn/IUserLocalCommunicationService.cpp
+++ b/app/src/main/cpp/skyline/services/ldn/IUserLocalCommunicationService.cpp
@@ -2,6 +2,7 @@
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "IUserLocalCommunicationService.h"
+#include
namespace skyline::service::ldn {
IUserLocalCommunicationService::IUserLocalCommunicationService(const DeviceState &state, ServiceManager &manager)
@@ -9,8 +10,39 @@ namespace skyline::service::ldn {
event{std::make_shared(state, false)} {}
Result IUserLocalCommunicationService::GetState(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
- static constexpr u32 StateNone{0x0};
- response.Push(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;
+ 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 latestUpdate(nodeBufferCount);
+
+ request.outputBuf.at(0).as() = 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 networkInfos(networkInfoSize);
+ request.outputBuf.at(0).copy_from(networkInfos);
+ response.Push(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;
}
}
diff --git a/app/src/main/cpp/skyline/services/ldn/IUserLocalCommunicationService.h b/app/src/main/cpp/skyline/services/ldn/IUserLocalCommunicationService.h
index fcedb2fe..4a54f78a 100644
--- a/app/src/main/cpp/skyline/services/ldn/IUserLocalCommunicationService.h
+++ b/app/src/main/cpp/skyline/services/ldn/IUserLocalCommunicationService.h
@@ -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 raw{};
+ };
+ static_assert(sizeof(MacAddress) == 0x6);
+
+ struct Ssid {
+ u8 length{};
+ std::array 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 ipv4Address;
+ MacAddress macAddress;
+ i8 nodeId;
+ u8 isConnected;
+ std::array username;
+ u8 _pad0_[0x1];
+ i16 localCommunicationVersion;
+ u8 _pad1_[0x10];
+ };
+ static_assert(sizeof(NodeInfo) == 0x40);
+
+ struct LdnNetworkInfo {
+ std::array securityParameter;
+ SecurityMode securityMode;
+ AcceptPolicy stationAcceptPolicy;
+ u8 hasActionFrame;
+ u8 _pad0_[0x2];
+ u8 nodeCountMax;
+ u8 nodeCount;
+ std::array nodes;
+ u8 _pad1_[0x2];
+ u16 advertiseDataSize;
+ std::array 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 passphrase;
+ };
+ static_assert(sizeof(SecurityConfig) == 0x44);
+
+ struct SecurityParameter {
+ std::array data;
+ SessionId sessionId;
+ };
+ static_assert(sizeof(SecurityParameter) == 0x20);
+
+ struct UserConfig {
+ std::array 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 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)
)
};
}
diff --git a/app/src/main/cpp/skyline/services/nifm/IGeneralService.cpp b/app/src/main/cpp/skyline/services/nifm/IGeneralService.cpp
index 636c6c3a..4e6e11ac 100644
--- a/app/src/main/cpp/skyline/services/nifm/IGeneralService.cpp
+++ b/app/src/main/cpp/skyline/services/nifm/IGeneralService.cpp
@@ -4,8 +4,22 @@
#include "IScanRequest.h"
#include "IRequest.h"
#include "IGeneralService.h"
+#include
+#include
namespace skyline::service::nifm {
+ /**
+ * @brief Converts integer value to an array of bytes ordered in little-endian format
+ */
+ static std::array ConvertIntToByteArray(i32 value) {
+ std::array 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(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() = 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(*state.settings->isInternetEnabled);
return {};
}
}
diff --git a/app/src/main/cpp/skyline/services/nifm/IGeneralService.h b/app/src/main/cpp/skyline/services/nifm/IGeneralService.h
index efdce9d0..e5f94e85 100644
--- a/app/src/main/cpp/skyline/services/nifm/IGeneralService.h
+++ b/app/src/main/cpp/skyline/services/nifm/IGeneralService.h
@@ -4,11 +4,90 @@
#pragma once
#include
+#include "common/uuid.h"
namespace skyline::service::nifm {
namespace result {
constexpr Result NoInternetConnection{110, 300};
}
+
+ struct IpAddressSetting {
+ bool isAutomatic{};
+ std::array currentAddress{};
+ std::array subnetMask{};
+ std::array gateway{};
+ };
+ static_assert(sizeof(IpAddressSetting) == 0xD);
+
+ struct DnsSetting {
+ bool isAutomatic{};
+ std::array primaryDns{};
+ std::array secondaryDns{};
+ };
+ static_assert(sizeof(DnsSetting) == 0x9);
+
+ struct ProxySetting {
+ bool enabled{};
+ u8 _pad0_[0x1];
+ u16 port{};
+ std::array proxyServer{};
+ bool automaticAuthEnabled{};
+ std::array user{};
+ std::array 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 ssid{};
+ u8 _unk0_[0x3];
+ std::array passphrase{};
+ };
+ static_assert(sizeof(SfWirelessSettingData) == 0x65);
+
+ struct NifmWirelessSettingData {
+ u8 ssidLength{};
+ std::array ssid{};
+ u8 _unk0_[0x1];
+ u8 _pad0_[0x1];
+ u32 _unk1_[0x2];
+ std::array passphrase{};
+ u8 _pad1_[0x3];
+ };
+ static_assert(sizeof(NifmWirelessSettingData) == 0x70);
+
+ #pragma pack(push, 1)
+ struct SfNetworkProfileData {
+ IpSettingData ipSettingData{};
+ UUID uuid{};
+ std::array networkName{};
+ u8 _unk0_[0x4];
+ SfWirelessSettingData wirelessSettingData{};
+ u8 _pad0_[0x1];
+ };
+ static_assert(sizeof(SfNetworkProfileData) == 0x17C);
+
+ struct NifmNetworkProfileData {
+ UUID uuid{};
+ std::array 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)
)
};
diff --git a/app/src/main/cpp/skyline/services/nifm/IRequest.cpp b/app/src/main/cpp/skyline/services/nifm/IRequest.cpp
index c627be49..1f00a180 100644
--- a/app/src/main/cpp/skyline/services/nifm/IRequest.cpp
+++ b/app/src/main/cpp/skyline/services/nifm/IRequest.cpp
@@ -3,6 +3,7 @@
#include
#include "IRequest.h"
+#include
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(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 {};
}
diff --git a/app/src/main/cpp/skyline/services/nifm/IRequest.h b/app/src/main/cpp/skyline/services/nifm/IRequest.h
index 5c3c15b5..49f19643 100644
--- a/app/src/main/cpp/skyline/services/nifm/IRequest.h
+++ b/app/src/main/cpp/skyline/services/nifm/IRequest.h
@@ -7,6 +7,17 @@
#include
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)
- )
+ )
};
}
diff --git a/app/src/main/cpp/skyline/services/ntc/IEnsureNetworkClockAvailabilityService.cpp b/app/src/main/cpp/skyline/services/ntc/IEnsureNetworkClockAvailabilityService.cpp
new file mode 100644
index 00000000..6b131e10
--- /dev/null
+++ b/app/src/main/cpp/skyline/services/ntc/IEnsureNetworkClockAvailabilityService.cpp
@@ -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
+
+namespace skyline::service::ntc {
+ IEnsureNetworkClockAvailabilityService::IEnsureNetworkClockAvailabilityService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager),
+ finishNotificationEvent(std::make_shared(state, false)) {}
+
+ Result IEnsureNetworkClockAvailabilityService::StartTask(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
+ if (!(*state.settings->isInternetEnabled))
+ return result::NetworkTimeNotAvailable;
+ finishNotificationEvent->Signal();
+ return {};
+ }
+}
diff --git a/app/src/main/cpp/skyline/services/ntc/IEnsureNetworkClockAvailabilityService.h b/app/src/main/cpp/skyline/services/ntc/IEnsureNetworkClockAvailabilityService.h
new file mode 100644
index 00000000..3dbf12dd
--- /dev/null
+++ b/app/src/main/cpp/skyline/services/ntc/IEnsureNetworkClockAvailabilityService.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
+
+#pragma once
+
+#include
+
+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 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)
+ )
+ };
+}
diff --git a/app/src/main/cpp/skyline/services/pctl/IParentalControlService.cpp b/app/src/main/cpp/skyline/services/pctl/IParentalControlService.cpp
index ef37aa2f..dbcd0bbd 100644
--- a/app/src/main/cpp/skyline/services/pctl/IParentalControlService.cpp
+++ b/app/src/main/cpp/skyline/services/pctl/IParentalControlService.cpp
@@ -11,7 +11,10 @@ namespace skyline::service::pctl {
}
Result IParentalControlService::CheckFreeCommunicationPermission(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
- response.Push(0);
+ return {};
+ }
+
+ Result IParentalControlService::EndFreeCommunication(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return {};
}
diff --git a/app/src/main/cpp/skyline/services/pctl/IParentalControlService.h b/app/src/main/cpp/skyline/services/pctl/IParentalControlService.h
index d1d9b6b2..62d116f5 100644
--- a/app/src/main/cpp/skyline/services/pctl/IParentalControlService.h
+++ b/app/src/main/cpp/skyline/services/pctl/IParentalControlService.h
@@ -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)
)
};
diff --git a/app/src/main/cpp/skyline/services/serviceman.cpp b/app/src/main/cpp/skyline/services/serviceman.cpp
index 3381f64d..4976e4fc 100644
--- a/app/src/main/cpp/skyline/services/serviceman.cpp
+++ b/app/src/main/cpp/skyline/services/serviceman.cpp
@@ -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(&name), sizeof(name)).as_string(true));
throw std::out_of_range(fmt::format("CreateService called with an unknown service name: {}", nameString));
diff --git a/app/src/main/cpp/skyline/services/socket/bsd/IClient.cpp b/app/src/main/cpp/skyline/services/socket/bsd/IClient.cpp
index 316daaae..a2558e7b 100644
--- a/app/src/main/cpp/skyline/services/socket/bsd/IClient.cpp
+++ b/app/src/main/cpp/skyline/services/socket/bsd/IClient.cpp
@@ -1,13 +1,14 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
+#include
#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(0);
+ response.Push(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 type{request.Pop()};
+ i32 protocol{request.Pop()};
+ 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 timeout{request.Pop()};
+
+ if (fdsCount == 0)
+ return PushBsdResult(response, -1, 0);
+
+ span outputBuf{request.outputBuf.at(0)};
+ auto fds{span(reinterpret_cast(outputBuf.data()), static_cast(fdsCount))};
+ i32 result{poll(fds.data(), static_cast(fdsCount), static_cast(timeout))};
+ return PushBsdResult(response, result, errno);
}
Result IClient::Recv(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
- return {};
+ i32 fd{request.Pop()};
+ i32 flags{request.Pop()};
+ 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 flags{request.Pop()};
+ 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 flags{request.Pop()};
+
+ 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(0);
- return {};
+ i32 fd{request.Pop()};
+ i32 flags{request.Pop()};
+
+ sockaddr addrIn{request.inputBuf.at(1).as()};
+ 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()};
+ 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()};
+ sockaddr addr{request.inputBuf.at(0).as()};
+ 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()};
+ sockaddr addr{request.inputBuf.at(0).as()};
+ 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()};
+ 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()};
+ 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 level{request.Pop()};
+ OptionName optionName{request.Pop()};
+ 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 backlog{request.Pop()};
+ 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 cmd{request.Pop()};
+ i32 arg{request.Pop()};
+ 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 level{request.Pop()};
+ OptionName optionName{request.Pop()};
+ if (level == 0xFFFF)
+ level = SOL_SOCKET;
+ i32 result{setsockopt(fd, level, GetOption(optionName), request.inputBuf.at(0).data(), static_cast(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 how{request.Pop()};
+ 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 flags{request.Pop()};
+ 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(0);
- response.Push(0);
- return {};
+ i32 fd{request.Pop()};
+ 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 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;
+ }
+ }
}
diff --git a/app/src/main/cpp/skyline/services/socket/bsd/IClient.h b/app/src/main/cpp/skyline/services/socket/bsd/IClient.h
index 9ed6e0cb..45d55da1 100644
--- a/app/src/main/cpp/skyline/services/socket/bsd/IClient.h
+++ b/app/src/main/cpp/skyline/services/socket/bsd/IClient.h
@@ -4,8 +4,18 @@
#pragma once
#include
+#include
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)
)
};
}
diff --git a/app/src/main/cpp/skyline/services/socket/nsd/IManager.cpp b/app/src/main/cpp/skyline/services/socket/nsd/IManager.cpp
index bee2c293..e6f786ce 100644
--- a/app/src/main/cpp/skyline/services/socket/nsd/IManager.cpp
+++ b/app/src/main/cpp/skyline/services/socket/nsd/IManager.cpp
@@ -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 {};
+ }
}
diff --git a/app/src/main/cpp/skyline/services/socket/nsd/IManager.h b/app/src/main/cpp/skyline/services/socket/nsd/IManager.h
index cf29b075..01ef46ca 100644
--- a/app/src/main/cpp/skyline/services/socket/nsd/IManager.h
+++ b/app/src/main/cpp/skyline/services/socket/nsd/IManager.h
@@ -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),
+ )
};
}
diff --git a/app/src/main/cpp/skyline/services/socket/sfdnsres/IResolver.cpp b/app/src/main/cpp/skyline/services/socket/sfdnsres/IResolver.cpp
index 34adbbd6..94af59da 100644
--- a/app/src/main/cpp/skyline/services/socket/sfdnsres/IResolver.cpp
+++ b/app/src/main/cpp/skyline/services/socket/sfdnsres/IResolver.cpp
@@ -2,7 +2,176 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "IResolver.h"
+#include
+#include
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(resultCode); // errno
+ response.Push(AddrInfoErrorToNetDbError(resultCode)); // NetDBErrorCode
+ response.Push(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(resultCode); // errno
+ response.Push(AddrInfoErrorToNetDbError(resultCode)); // NetDBErrorCode
+ response.Push(dataSize);
+ response.Push(0);
+ return {};
+ }
+
+ Result IResolver::GetNameInfoRequestWithOptions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
+ return {};
+ }
+
+ std::pair 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 data = SerializeAddrInfo(result, resultCode, hostname);
+ dataSize = static_cast(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 IResolver::SerializeAddrInfo(const addrinfo* addrinfo, i32 result_code, std::string_view host) {
+ std::vector 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(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(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;
+ }
}
diff --git a/app/src/main/cpp/skyline/services/socket/sfdnsres/IResolver.h b/app/src/main/cpp/skyline/services/socket/sfdnsres/IResolver.h
index 37734d56..1cddf6a9 100644
--- a/app/src/main/cpp/skyline/services/socket/sfdnsres/IResolver.h
+++ b/app/src/main/cpp/skyline/services/socket/sfdnsres/IResolver.h
@@ -4,13 +4,45 @@
#pragma once
#include
+#include
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 GetAddrInfoRequestImpl(ipc::IpcRequest &request);
+
+ std::vector 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)
+ )
};
}
diff --git a/app/src/main/cpp/skyline/services/ssl/ISslContext.cpp b/app/src/main/cpp/skyline/services/ssl/ISslContext.cpp
index eb0adba7..04351d15 100644
--- a/app/src/main/cpp/skyline/services/ssl/ISslContext.cpp
+++ b/app/src/main/cpp/skyline/services/ssl/ISslContext.cpp
@@ -17,4 +17,8 @@ namespace skyline::service::ssl {
response.Push(0);
return {};
}
+
+ Result ISslContext::RegisterInternalPki(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
+ return {};
+ }
}
diff --git a/app/src/main/cpp/skyline/services/ssl/ISslContext.h b/app/src/main/cpp/skyline/services/ssl/ISslContext.h
index 15dd589c..273734ab 100644
--- a/app/src/main/cpp/skyline/services/ssl/ISslContext.h
+++ b/app/src/main/cpp/skyline/services/ssl/ISslContext.h
@@ -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)
)
};
}
diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt
index 41a42742..65ede7a9 100644
--- a/app/src/main/java/emu/skyline/EmulationActivity.kt
+++ b/app/src/main/java/emu/skyline/EmulationActivity.kt
@@ -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() }
diff --git a/app/src/main/java/emu/skyline/settings/EmulationSettings.kt b/app/src/main/java/emu/skyline/settings/EmulationSettings.kt
index 3ba61123..362992fe 100644
--- a/app/src/main/java/emu/skyline/settings/EmulationSettings.kt
+++ b/app/src/main/java/emu/skyline/settings/EmulationSettings.kt
@@ -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)
diff --git a/app/src/main/java/emu/skyline/settings/NativeSettings.kt b/app/src/main/java/emu/skyline/settings/NativeSettings.kt
index ad75b564..7e6ab353 100644
--- a/app/src/main/java/emu/skyline/settings/NativeSettings.kt
+++ b/app/src/main/java/emu/skyline/settings/NativeSettings.kt
@@ -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),
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e5ec230a..d8f7f9d1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -80,6 +80,7 @@
Profile Picture
System Language
System Region
+ The system will be able to use internet
Display
Show Performance Statistics
diff --git a/app/src/main/res/xml/emulation_preferences.xml b/app/src/main/res/xml/emulation_preferences.xml
index 7cb111a3..1a08e421 100644
--- a/app/src/main/res/xml/emulation_preferences.xml
+++ b/app/src/main/res/xml/emulation_preferences.xml
@@ -32,6 +32,11 @@
app:key="system_region"
app:title="@string/system_region"
app:useSimpleSummaryProvider="true" />
+