diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 4e3a614a4..5a684c428 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-FileCopyrightText: 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -89,6 +90,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Service, BGTC) \ SUB(Service, BTDRV) \ SUB(Service, BTM) \ + SUB(Service, BSD) \ SUB(Service, Capture) \ SUB(Service, ERPT) \ SUB(Service, ETicket) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 9e2f42c5d..8ee68ec48 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -57,6 +58,7 @@ enum class Class : u8 { Service_BPC, ///< The BPC service Service_BTDRV, ///< The Bluetooth driver service Service_BTM, ///< The BTM service + Service_BSD, ///< The BSD sockets service Service_Capture, ///< The capture service Service_ERPT, ///< The error reporting service Service_ETicket, ///< The ETicket service diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6f20c1352..1e453b381 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,4 +1,5 @@ # SPDX-FileCopyrightText: 2018 yuzu Emulator Project +# SPDX-FileCopyrightText: 2025 citron Emulator Project # SPDX-License-Identifier: GPL-2.0-or-later add_library(core STATIC @@ -776,8 +777,12 @@ add_library(core STATIC hle/service/ngc/ngc.h hle/service/nifm/nifm.cpp hle/service/nifm/nifm.h + hle/service/nifm/nifm_utils.cpp + hle/service/nifm/nifm_utils.h hle/service/nim/nim.cpp hle/service/nim/nim.h + hle/service/nim/nim_utils.cpp + hle/service/nim/nim_utils.h hle/service/npns/npns.cpp hle/service/npns/npns.h hle/service/ns/account_proxy_interface.cpp @@ -1061,6 +1066,8 @@ add_library(core STATIC hle/service/sockets/sockets.h hle/service/sockets/sockets_translate.cpp hle/service/sockets/sockets_translate.h + hle/service/sockets/socket_utils.cpp + hle/service/sockets/socket_utils.h hle/service/spl/csrng.cpp hle/service/spl/csrng.h hle/service/spl/spl.cpp diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 4f717c871..7b1102ebc 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" @@ -6,6 +7,7 @@ #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nifm/nifm.h" +#include "core/hle/service/nifm/nifm_utils.h" #include "core/hle/service/server_manager.h" #include "network/network.h" diff --git a/src/core/hle/service/nifm/nifm_utils.cpp b/src/core/hle/service/nifm/nifm_utils.cpp new file mode 100644 index 000000000..bb03ec636 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_utils.cpp @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "common/logging/log.h" +#include "core/hle/service/nifm/nifm_utils.h" + +namespace Service::NIFM::nn::nifm { + +// Simple implementation to track network requests +namespace { + std::mutex g_request_mutex; + std::map g_requests; + u32 g_next_request_id = 1; + bool g_network_available = true; // Default to true for emulation +} + +bool IsNetworkAvailable() { + // For emulation purposes, we'll just return the mocked availability + std::lock_guard lock(g_request_mutex); + return g_network_available; +} + +u32 SubmitNetworkRequest() { + std::lock_guard lock(g_request_mutex); + + if (!g_network_available) { + LOG_WARNING(Service_NIFM, "Network request submitted but network is not available"); + } + + u32 request_id = g_next_request_id++; + + NetworkRequest request{ + .request_id = request_id, + .is_pending = true, + .result = NetworkRequestResult::Success // Assume immediate success for emulation + }; + + g_requests[request_id] = request; + + LOG_INFO(Service_NIFM, "Network request submitted with ID: {}", request_id); + return request_id; +} + +NetworkRequestResult GetNetworkRequestResult(u32 request_id) { + std::lock_guard lock(g_request_mutex); + + auto it = g_requests.find(request_id); + if (it == g_requests.end()) { + LOG_ERROR(Service_NIFM, "Tried to get result for invalid request ID: {}", request_id); + return NetworkRequestResult::Error; + } + + // For emulation, we'll mark the request as no longer pending once the result is checked + it->second.is_pending = false; + + return it->second.result; +} + +bool CancelNetworkRequest(u32 request_id) { + std::lock_guard lock(g_request_mutex); + + auto it = g_requests.find(request_id); + if (it == g_requests.end()) { + LOG_ERROR(Service_NIFM, "Tried to cancel invalid request ID: {}", request_id); + return false; + } + + if (!it->second.is_pending) { + LOG_WARNING(Service_NIFM, "Tried to cancel a request that is not pending, ID: {}", request_id); + return false; + } + + it->second.is_pending = false; + it->second.result = NetworkRequestResult::Canceled; + + LOG_INFO(Service_NIFM, "Network request canceled with ID: {}", request_id); + return true; +} + +} // namespace Service::NIFM::nn::nifm diff --git a/src/core/hle/service/nifm/nifm_utils.h b/src/core/hle/service/nifm/nifm_utils.h new file mode 100644 index 000000000..2ca63ffdd --- /dev/null +++ b/src/core/hle/service/nifm/nifm_utils.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::NIFM { + +// Network request result codes +enum class NetworkRequestResult { + Success = 0, + Error = 1, + Canceled = 2, + Timeout = 3, +}; + +// Network request structure +struct NetworkRequest { + u32 request_id; + bool is_pending; + NetworkRequestResult result; +}; + +namespace nn::nifm { + +// Checks if network connectivity is available +bool IsNetworkAvailable(); + +// Submits a network connection request +// Returns the request ID or 0 if the request failed +u32 SubmitNetworkRequest(); + +// Gets the status of a network request +// Returns the request result +NetworkRequestResult GetNetworkRequestResult(u32 request_id); + +// Cancels a pending network request +// Returns true if the request was successfully canceled +bool CancelNetworkRequest(u32 request_id); + +} // namespace nn::nifm + +} // namespace Service::NIFM diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 15cf2e022..ce8e1e9b1 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -8,6 +9,7 @@ #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nim/nim.h" +#include "core/hle/service/nim/nim_utils.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/nim/nim_utils.cpp b/src/core/hle/service/nim/nim_utils.cpp new file mode 100644 index 000000000..128092afe --- /dev/null +++ b/src/core/hle/service/nim/nim_utils.cpp @@ -0,0 +1,124 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "common/logging/log.h" +#include "core/hle/service/nim/nim_utils.h" + +namespace Service::NIM::nn::nim { + +// Simple implementation to track installation tasks +namespace { + std::mutex g_task_mutex; + std::map g_tasks; + u64 g_next_task_id = 1; + bool g_service_available = true; // Default to true for emulation +} + +bool IsServiceAvailable() { + std::lock_guard lock(g_task_mutex); + return g_service_available; +} + +u64 CreateInstallTask(u64 application_id) { + std::lock_guard lock(g_task_mutex); + + if (!g_service_available) { + LOG_WARNING(Service_NIM, "Installation task creation attempted but service is not available"); + return 0; + } + + u64 task_id = g_next_task_id++; + + Task task{ + .task_id = task_id, + .progress = { + .downloaded_bytes = 0, + .total_bytes = 1'000'000'000, // Fake 1GB download size + .status = TaskStatus::None + } + }; + + g_tasks[task_id] = task; + + LOG_INFO(Service_NIM, "Installation task created for application 0x{:016X} with ID: {}", + application_id, task_id); + return task_id; +} + +TaskProgress GetTaskProgress(u64 task_id) { + std::lock_guard lock(g_task_mutex); + + auto it = g_tasks.find(task_id); + if (it == g_tasks.end()) { + LOG_ERROR(Service_NIM, "Tried to get progress for invalid task ID: {}", task_id); + return {0, 0, TaskStatus::Failed}; + } + + // If task is in download state, simulate progress + if (it->second.progress.status == TaskStatus::Downloading) { + // Simulate download progress (add 10% of total size) + auto& progress = it->second.progress; + const u64 increment = progress.total_bytes / 10; + + progress.downloaded_bytes += increment; + if (progress.downloaded_bytes >= progress.total_bytes) { + progress.downloaded_bytes = progress.total_bytes; + progress.status = TaskStatus::Installing; + LOG_INFO(Service_NIM, "Task ID {} download complete, now installing", task_id); + } + } else if (it->second.progress.status == TaskStatus::Installing) { + // Simulate installation completion + it->second.progress.status = TaskStatus::Complete; + LOG_INFO(Service_NIM, "Task ID {} installation complete", task_id); + } + + return it->second.progress; +} + +bool StartInstallTask(u64 task_id) { + std::lock_guard lock(g_task_mutex); + + auto it = g_tasks.find(task_id); + if (it == g_tasks.end()) { + LOG_ERROR(Service_NIM, "Tried to start invalid task ID: {}", task_id); + return false; + } + + if (it->second.progress.status != TaskStatus::None && + it->second.progress.status != TaskStatus::Pending) { + LOG_WARNING(Service_NIM, "Tried to start task ID {} which is already in progress", task_id); + return false; + } + + it->second.progress.status = TaskStatus::Downloading; + LOG_INFO(Service_NIM, "Started installation task ID: {}", task_id); + + return true; +} + +bool CancelInstallTask(u64 task_id) { + std::lock_guard lock(g_task_mutex); + + auto it = g_tasks.find(task_id); + if (it == g_tasks.end()) { + LOG_ERROR(Service_NIM, "Tried to cancel invalid task ID: {}", task_id); + return false; + } + + if (it->second.progress.status == TaskStatus::Complete || + it->second.progress.status == TaskStatus::Failed || + it->second.progress.status == TaskStatus::Canceled) { + LOG_WARNING(Service_NIM, "Tried to cancel task ID {} which is already in a final state", task_id); + return false; + } + + it->second.progress.status = TaskStatus::Canceled; + LOG_INFO(Service_NIM, "Canceled installation task ID: {}", task_id); + + return true; +} + +} // namespace Service::NIM::nn::nim diff --git a/src/core/hle/service/nim/nim_utils.h b/src/core/hle/service/nim/nim_utils.h new file mode 100644 index 000000000..00816b1c4 --- /dev/null +++ b/src/core/hle/service/nim/nim_utils.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Service::NIM { + +// Network installation task status +enum class TaskStatus { + None = 0, + Pending = 1, + Downloading = 2, + Installing = 3, + Complete = 4, + Failed = 5, + Canceled = 6, +}; + +// Network installation task progress +struct TaskProgress { + u64 downloaded_bytes; + u64 total_bytes; + TaskStatus status; +}; + +// Network installation task +struct Task { + u64 task_id; + TaskProgress progress; +}; + +namespace nn::nim { + +// Checks if the NIM service is available +bool IsServiceAvailable(); + +// Creates a new installation task +// Returns the task ID or 0 if the task creation failed +u64 CreateInstallTask(u64 application_id); + +// Gets the progress of an installation task +// Returns the task progress +TaskProgress GetTaskProgress(u64 task_id); + +// Starts an installation task +// Returns true if the task was successfully started +bool StartInstallTask(u64 task_id); + +// Cancels an installation task +// Returns true if the task was successfully canceled +bool CancelInstallTask(u64 task_id); + +} // namespace nn::nim + +} // namespace Service::NIM \ No newline at end of file diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index b2c9c60a1..ef2c36251 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -1077,15 +1077,29 @@ void BSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noex } void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) { + // Iterate through all file descriptors and pass the packet to each valid socket for (auto& optional_descriptor : file_descriptors) { if (!optional_descriptor.has_value()) { continue; } + FileDescriptor& descriptor = *optional_descriptor; - descriptor.socket.get()->HandleProxyPacket(packet); + if (descriptor.socket) { + descriptor.socket->HandleProxyPacket(packet); + } } } +s32 BSD::Connect(s32 socket, const SockAddrIn& addr) { + // Call ConnectImpl directly if possible, or return error + + LOG_INFO(Service_BSD, "nn::socket::Connect called for socket {} with address {}:{}", + socket, addr.ip[0], addr.portno); + + // For now, we're assuming the connection will succeed return 0 + return 0; +} + BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { // clang-format off diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index c4f930155..0421a9ad4 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -38,6 +38,9 @@ public: Errno CloseImpl(s32 fd); std::optional> GetSocket(s32 fd); + // Static function that can be called from nn::socket::Connect + static s32 Connect(s32 socket, const SockAddrIn& addr); + private: /// Maximum number of file descriptors static constexpr size_t MAX_FD = 128; diff --git a/src/core/hle/service/sockets/socket_utils.cpp b/src/core/hle/service/sockets/socket_utils.cpp new file mode 100644 index 000000000..02d43db9d --- /dev/null +++ b/src/core/hle/service/sockets/socket_utils.cpp @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include + +#include "common/logging/log.h" +#include "core/hle/service/sockets/bsd.h" +#include "core/hle/service/sockets/socket_utils.h" + +namespace Service::Sockets::nn::socket { + +bool InetAton(const char* ip, in_addr* addr) { + if (ip == nullptr || addr == nullptr) { + return false; + } + + std::string_view ip_view(ip); + + // Count the number of dots to validate IPv4 format + size_t dots = std::count(ip_view.begin(), ip_view.end(), '.'); + if (dots != 3) { + return false; + } + + // Parse the IP address in standard dotted-decimal notation + u32 result = 0; + size_t pos = 0; + + for (int i = 0; i < 4; i++) { + size_t next_dot = ip_view.find('.', pos); + std::string_view octet_view; + + if (i < 3) { + if (next_dot == std::string_view::npos) { + return false; + } + octet_view = ip_view.substr(pos, next_dot - pos); + pos = next_dot + 1; + } else { + octet_view = ip_view.substr(pos); + } + + u32 octet; + auto [ptr, ec] = std::from_chars(octet_view.data(), octet_view.data() + octet_view.size(), octet); + if (ec != std::errc() || octet > 255 || (ptr != octet_view.data() + octet_view.size())) { + return false; + } + + result = (result << 8) | octet; + } + + addr->s_addr = result; + return true; +} + +s32 Connect(s32 socket, const sockaddr* addr, u32 addr_len) { + if (addr == nullptr || addr_len < sizeof(sockaddr)) { + LOG_ERROR(Service_BSD, "Invalid address pointer or length"); + // Set errno to EINVAL (Invalid argument) + errno = static_cast(Errno::INVAL); + return -1; + } + + // Create a BSD-compliant sockaddr_in from our sockaddr + SockAddrIn bsd_addr{}; + bsd_addr.len = sizeof(SockAddrIn); + // Cast explicitly with a mask to ensure valid range conversion + bsd_addr.family = static_cast(addr->sa_family & 0xFF); + + if (addr->sa_family == 2) { // AF_INET + const auto* addr_in = reinterpret_cast(addr); + bsd_addr.portno = addr_in->sin_port; + + // Copy IPv4 address (in network byte order) + const u32 ip_addr = addr_in->sin_addr.s_addr; + bsd_addr.ip[0] = static_cast((ip_addr >> 24) & 0xFF); + bsd_addr.ip[1] = static_cast((ip_addr >> 16) & 0xFF); + bsd_addr.ip[2] = static_cast((ip_addr >> 8) & 0xFF); + bsd_addr.ip[3] = static_cast(ip_addr & 0xFF); + } else { + LOG_ERROR(Service_BSD, "Unsupported address family: {}", addr->sa_family); + // Set errno to EAFNOSUPPORT (Address family not supported) + errno = static_cast(Errno::INVAL); // Using INVAL as a substitute for EAFNOSUPPORT + return -1; + } + + // Forward to the BSD socket implementation + return BSD::Connect(socket, bsd_addr); +} + +} // namespace Service::Sockets::nn::socket diff --git a/src/core/hle/service/sockets/socket_utils.h b/src/core/hle/service/sockets/socket_utils.h new file mode 100644 index 000000000..d95d896ca --- /dev/null +++ b/src/core/hle/service/sockets/socket_utils.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include +#include + +namespace Service::Sockets { + +// Base socket structures and utilities for nn::socket + +// in_addr struct similar to standard BSD/POSIX implementation +struct in_addr { + u32 s_addr; +}; + +// sockaddr struct similar to standard BSD/POSIX implementation +struct sockaddr { + u16 sa_family; + char sa_data[14]; +}; + +// sockaddr_in struct similar to standard BSD/POSIX implementation +struct sockaddr_in { + u16 sin_family; + u16 sin_port; + in_addr sin_addr; + char sin_zero[8]; +}; + +// Socket configuration data based on LibraryConfigData from switchbrew +struct Config { + u32 version; + u32 tcp_tx_buf_size; + u32 tcp_rx_buf_size; + u32 tcp_tx_buf_max_size; + u32 tcp_rx_buf_max_size; + u32 udp_tx_buf_size; + u32 udp_rx_buf_size; + u32 sb_efficiency; +}; + +namespace nn::socket { + +// InetAton converts an IPv4 address string to an in_addr structure +// Returns true on success, false on failure +bool InetAton(const char* ip, in_addr* addr); + +// Connect to a remote host +// Returns 0 on success, -1 on failure +s32 Connect(s32 socket, const sockaddr* addr, u32 addr_len); + +} // namespace nn::socket + +} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp index ba42ffebc..b71b2cb3b 100644 --- a/src/core/hle/service/sockets/sockets.cpp +++ b/src/core/hle/service/sockets/sockets.cpp @@ -10,6 +10,7 @@ #include "core/hle/service/sockets/ethc.h" #include "core/hle/service/sockets/nsd.h" #include "core/hle/service/sockets/sfdnsres.h" +#include "core/hle/service/sockets/socket_utils.h" #include "core/hle/service/sockets/sockets.h" namespace Service::Sockets {