Compare commits
	
		
			1 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | e5495cbc4b | 
| @@ -53,8 +53,7 @@ add_library(citra_common STATIC | |||||||
|     archives.h |     archives.h | ||||||
|     assert.h |     assert.h | ||||||
|     atomic_ops.h |     atomic_ops.h | ||||||
|     detached_tasks.cpp |     bit_cast.h | ||||||
|     detached_tasks.h |  | ||||||
|     bit_field.h |     bit_field.h | ||||||
|     bit_set.h |     bit_set.h | ||||||
|     bounded_threadsafe_queue.h |     bounded_threadsafe_queue.h | ||||||
| @@ -66,6 +65,8 @@ add_library(citra_common STATIC | |||||||
|     common_precompiled_headers.h |     common_precompiled_headers.h | ||||||
|     common_types.h |     common_types.h | ||||||
|     construct.h |     construct.h | ||||||
|  |     detached_tasks.cpp | ||||||
|  |     detached_tasks.h | ||||||
|     dynamic_library/dynamic_library.cpp |     dynamic_library/dynamic_library.cpp | ||||||
|     dynamic_library/dynamic_library.h |     dynamic_library/dynamic_library.h | ||||||
|     dynamic_library/fdk-aac.cpp |     dynamic_library/fdk-aac.cpp | ||||||
| @@ -111,6 +112,7 @@ add_library(citra_common STATIC | |||||||
|     settings.cpp |     settings.cpp | ||||||
|     settings.h |     settings.h | ||||||
|     slot_vector.h |     slot_vector.h | ||||||
|  |     socket_types.h | ||||||
|     serialization/atomic.h |     serialization/atomic.h | ||||||
|     serialization/boost_discrete_interval.hpp |     serialization/boost_discrete_interval.hpp | ||||||
|     serialization/boost_flat_set.h |     serialization/boost_flat_set.h | ||||||
|   | |||||||
| @@ -13,26 +13,20 @@ | |||||||
| // lambda and force the compiler to not inline it. | // lambda and force the compiler to not inline it. | ||||||
|  |  | ||||||
| #define ASSERT(_a_)                                                                                \ | #define ASSERT(_a_)                                                                                \ | ||||||
|     do                                                                                             \ | ([&]() CITRA_NO_INLINE {                                                                        \ | ||||||
|         if (!(_a_)) [[unlikely]] {                                                                 \ |         if (!(_a_)) [[unlikely]] {                                                                 \ | ||||||
|             []() CITRA_NO_INLINE CITRA_NO_RETURN {                                                 \ |             LOG_CRITICAL(Debug, "Assertion Failed!");                                              \ | ||||||
|                 LOG_CRITICAL(Debug, "Assertion Failed!");                                          \ |             Crash();                                                                    \ | ||||||
|                 Crash();                                                                           \ |     }                                                                                          \ | ||||||
|                 exit(1);                                                                           \ | }()) | ||||||
|             }();                                                                                   \ |  | ||||||
|         }                                                                                          \ |  | ||||||
|     while (0) |  | ||||||
|  |  | ||||||
| #define ASSERT_MSG(_a_, ...)                                                                       \ | #define ASSERT_MSG(_a_, ...)                                                                       \ | ||||||
|     do                                                                                             \ |     ([&]() CITRA_NO_INLINE {                                                                        \ | ||||||
|         if (!(_a_)) [[unlikely]] {                                                                 \ |             if (!(_a_)) [[unlikely]] {                                                                 \ | ||||||
|             [&]() CITRA_NO_INLINE CITRA_NO_RETURN {                                                \ |                 LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__);                                \ | ||||||
|                 LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__);                            \ |                 Crash();                                                                    \ | ||||||
|                 Crash();                                                                           \ |  | ||||||
|                 exit(1);                                                                           \ |  | ||||||
|             }();                                                                                   \ |  | ||||||
|         }                                                                                          \ |         }                                                                                          \ | ||||||
|     while (0) |     }()) | ||||||
|  |  | ||||||
| #define UNREACHABLE()                                                                              \ | #define UNREACHABLE()                                                                              \ | ||||||
|     ([]() CITRA_NO_INLINE CITRA_NO_RETURN {                                                        \ |     ([]() CITRA_NO_INLINE CITRA_NO_RETURN {                                                        \ | ||||||
| @@ -58,3 +52,6 @@ | |||||||
|  |  | ||||||
| #define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!") | #define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!") | ||||||
| #define UNIMPLEMENTED_MSG(_a_, ...) LOG_CRITICAL(Debug, _a_, __VA_ARGS__) | #define UNIMPLEMENTED_MSG(_a_, ...) LOG_CRITICAL(Debug, _a_, __VA_ARGS__) | ||||||
|  |  | ||||||
|  | #define UNIMPLEMENTED_IF(cond) ASSERT_MSG(!(cond), "Unimplemented code!") | ||||||
|  | #define UNIMPLEMENTED_IF_MSG(cond, ...) ASSERT_MSG(!(cond), __VA_ARGS__) | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								src/common/bit_cast.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/common/bit_cast.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <version> | ||||||
|  |  | ||||||
|  | #ifdef __cpp_lib_bit_cast | ||||||
|  | #include <bit> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace Common { | ||||||
|  |  | ||||||
|  | template <typename To, typename From> | ||||||
|  | constexpr inline To BitCast(const From& from) { | ||||||
|  | #ifdef __cpp_lib_bit_cast | ||||||
|  |     return std::bit_cast<To>(from); | ||||||
|  | #else | ||||||
|  |     return __builtin_bit_cast(To, from); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Common | ||||||
| @@ -110,6 +110,15 @@ __declspec(dllimport) void __stdcall DebugBreak(void); | |||||||
|         return static_cast<T>(key) == 0;                                                           \ |         return static_cast<T>(key) == 0;                                                           \ | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #define CITRA_NON_COPYABLE(cls)                                                                    \ | ||||||
|  |     cls(const cls&) = delete;                                                                      \ | ||||||
|  |     cls& operator=(const cls&) = delete | ||||||
|  |  | ||||||
|  | #define CITRA_NON_MOVEABLE(cls)                                                                    \ | ||||||
|  |     cls(cls&&) = delete;                                                                           \ | ||||||
|  |     cls& operator=(cls&&) = delete | ||||||
|  |  | ||||||
|  |  | ||||||
| // Generic function to get last error message. | // Generic function to get last error message. | ||||||
| // Call directly after the command or use the error num. | // Call directly after the command or use the error num. | ||||||
| // This function might change the error code. | // This function might change the error code. | ||||||
|   | |||||||
| @@ -505,6 +505,9 @@ struct Values { | |||||||
|     // Miscellaneous |     // Miscellaneous | ||||||
|     Setting<std::string> log_filter{"*:Info", "log_filter"}; |     Setting<std::string> log_filter{"*:Info", "log_filter"}; | ||||||
|  |  | ||||||
|  |     // Network | ||||||
|  |     Setting<std::string> network_interface{std::string(), "network_interface"}; | ||||||
|  |  | ||||||
|     // Video Dumping |     // Video Dumping | ||||||
|     std::string output_format; |     std::string output_format; | ||||||
|     std::string format_options; |     std::string format_options; | ||||||
|   | |||||||
							
								
								
									
										143
									
								
								src/common/socket_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/common/socket_types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/swap.h" | ||||||
|  |  | ||||||
|  | namespace Network { | ||||||
|  |  | ||||||
|  | /// Address families | ||||||
|  | enum class Domain : u32 { | ||||||
|  |     UNSPEC = 0, | ||||||
|  |     INET = 2, ///< Address family for IPv4 | ||||||
|  |     INET6 = 23, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /// Socket types | ||||||
|  | enum class Type : u32 { | ||||||
|  |     STREAM = 1, | ||||||
|  |     DGRAM = 2, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /// Shutdown mode | ||||||
|  | enum class ShutdownHow { | ||||||
|  |     RD, | ||||||
|  |     WR, | ||||||
|  |     RDWR, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum class FcntlCmd : u32 { | ||||||
|  |     GETFL = 3, | ||||||
|  |     SETFL = 4, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum class SendFlags : u32 { | ||||||
|  |     OOB = 1, | ||||||
|  |     PEEK = 2, | ||||||
|  |     DONTWAIT = 4, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | DECLARE_ENUM_FLAG_OPERATORS(SendFlags) | ||||||
|  |  | ||||||
|  | /// Array of IPv4 address | ||||||
|  | using IPv4Address = std::array<u8, 4>; | ||||||
|  |  | ||||||
|  | /// Union to represent the 3DS sockaddr structure | ||||||
|  | union CTRSockAddr { | ||||||
|  |     /// Structure to represent a raw sockaddr | ||||||
|  |     struct { | ||||||
|  |         u8 len;           ///< The length of the entire structure, only the set fields count | ||||||
|  |         u8 sa_family;     ///< The address family of the sockaddr | ||||||
|  |         u8 sa_data[0x1A]; ///< The extra data, this varies, depending on the address family | ||||||
|  |     } raw; | ||||||
|  |  | ||||||
|  |     /// Structure to represent the 3ds' sockaddr_in structure | ||||||
|  |     struct CTRSockAddrIn { | ||||||
|  |         u8 len;        ///< The length of the entire structure | ||||||
|  |         u8 sin_family; ///< The address family of the sockaddr_in | ||||||
|  |         u16 sin_port;  ///< The port associated with this sockaddr_in | ||||||
|  |         u32 sin_addr;  ///< The actual address of the sockaddr_in | ||||||
|  |     } in; | ||||||
|  |     static_assert(sizeof(CTRSockAddrIn) == 8, "Invalid CTRSockAddrIn size"); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct CTRAddrInfo { | ||||||
|  |     s32_le ai_flags; | ||||||
|  |     s32_le ai_family; | ||||||
|  |     s32_le ai_socktype; | ||||||
|  |     s32_le ai_protocol; | ||||||
|  |     s32_le ai_addrlen; | ||||||
|  |     char ai_canonname[256]; | ||||||
|  |     CTRSockAddr ai_addr; | ||||||
|  |  | ||||||
|  |     static u32 AddressInfoFlagsToPlatform(u32 flags) { | ||||||
|  |         u32 ret = 0; | ||||||
|  |         if (flags & 1) { | ||||||
|  |             ret |= AI_PASSIVE; | ||||||
|  |         } | ||||||
|  |         if (flags & 2) { | ||||||
|  |             ret |= AI_CANONNAME; | ||||||
|  |         } | ||||||
|  |         if (flags & 4) { | ||||||
|  |             ret |= AI_NUMERICHOST; | ||||||
|  |         } | ||||||
|  |         if (flags & 8) { | ||||||
|  |             ret |= AI_NUMERICSERV; | ||||||
|  |         } | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static u32 AddressInfoFlagsFromPlatform(u32 flags) { | ||||||
|  |         u32 ret = 0; | ||||||
|  |         if (flags & AI_PASSIVE) { | ||||||
|  |             ret |= 1; | ||||||
|  |         } | ||||||
|  |         if (flags & AI_CANONNAME) { | ||||||
|  |             ret |= 2; | ||||||
|  |         } | ||||||
|  |         if (flags & AI_NUMERICHOST) { | ||||||
|  |             ret |= 4; | ||||||
|  |         } | ||||||
|  |         if (flags & AI_NUMERICSERV) { | ||||||
|  |             ret |= 8; | ||||||
|  |         } | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |            /// Converts a platform-specific addrinfo to a 3ds addrinfo. | ||||||
|  |     static CTRAddrInfo FromPlatform(const addrinfo& addr) { | ||||||
|  |         CTRAddrInfo ctr_addr{ | ||||||
|  |             .ai_flags = static_cast<s32_le>(AddressInfoFlagsFromPlatform(addr.ai_flags)), | ||||||
|  |             .ai_family = static_cast<s32_le>(SocketDomainFromPlatform(addr.ai_family)), | ||||||
|  |             .ai_socktype = static_cast<s32_le>(SocketTypeFromPlatform(addr.ai_socktype)), | ||||||
|  |             .ai_protocol = static_cast<s32_le>(SocketProtocolFromPlatform(addr.ai_protocol)), | ||||||
|  |             .ai_addr = CTRSockAddr::FromPlatform(*addr.ai_addr), | ||||||
|  |         }; | ||||||
|  |         ctr_addr.ai_addrlen = static_cast<s32_le>(ctr_addr.ai_addr.raw.len); | ||||||
|  |         if (addr.ai_canonname) | ||||||
|  |             std::strncpy(ctr_addr.ai_canonname, addr.ai_canonname, | ||||||
|  |                          sizeof(ctr_addr.ai_canonname) - 1); | ||||||
|  |         return ctr_addr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |            /// Converts a platform-specific addrinfo to a 3ds addrinfo. | ||||||
|  |     static addrinfo ToPlatform(const CTRAddrInfo& ctr_addr) { | ||||||
|  |         // Only certain fields are meaningful in hints, copy them manually | ||||||
|  |         addrinfo addr = { | ||||||
|  |             .ai_flags = static_cast<int>(AddressInfoFlagsToPlatform(ctr_addr.ai_flags)), | ||||||
|  |             .ai_family = static_cast<int>(SocketDomainToPlatform(ctr_addr.ai_family)), | ||||||
|  |             .ai_socktype = static_cast<int>(SocketTypeToPlatform(ctr_addr.ai_socktype)), | ||||||
|  |             .ai_protocol = static_cast<int>(SocketProtocolToPlatform(ctr_addr.ai_protocol)), | ||||||
|  |         }; | ||||||
|  |         return addr; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(CTRAddrInfo) == 0x130, "Size of CTRAddrInfo is not correct"); | ||||||
|  |  | ||||||
|  | constexpr u32 FLAG_MSG_PEEK = 0x2; | ||||||
|  | constexpr u32 FLAG_MSG_DONTWAIT = 0x80; | ||||||
|  | constexpr u32 FLAG_O_NONBLOCK = 0x800; | ||||||
|  |  | ||||||
|  | } // namespace Network | ||||||
| @@ -439,6 +439,11 @@ add_library(citra_core STATIC | |||||||
|     hw/rsa/rsa.h |     hw/rsa/rsa.h | ||||||
|     hw/y2r.cpp |     hw/y2r.cpp | ||||||
|     hw/y2r.h |     hw/y2r.h | ||||||
|  |     network/network.cpp | ||||||
|  |     network/network.h | ||||||
|  |     network/network_interface.cpp | ||||||
|  |     network/network_interface.h | ||||||
|  |     network/sockets.h | ||||||
|     loader/3dsx.cpp |     loader/3dsx.cpp | ||||||
|     loader/3dsx.h |     loader/3dsx.h | ||||||
|     loader/elf.cpp |     loader/elf.cpp | ||||||
|   | |||||||
| @@ -304,13 +304,13 @@ MappedBuffer::MappedBuffer(Memory::MemorySystem& memory, std::shared_ptr<Process | |||||||
|     perms = desc.perms; |     perms = desc.perms; | ||||||
| } | } | ||||||
|  |  | ||||||
| void MappedBuffer::Read(void* dest_buffer, std::size_t offset, std::size_t size) { | void MappedBuffer::Read(void* dest_buffer, std::size_t offset, std::size_t size) const { | ||||||
|     ASSERT(perms & IPC::R); |     ASSERT(perms & IPC::R); | ||||||
|     ASSERT(offset + size <= this->size); |     ASSERT(offset + size <= this->size); | ||||||
|     memory->ReadBlock(*process, address + static_cast<VAddr>(offset), dest_buffer, size); |     memory->ReadBlock(*process, address + static_cast<VAddr>(offset), dest_buffer, size); | ||||||
| } | } | ||||||
|  |  | ||||||
| void MappedBuffer::Write(const void* src_buffer, std::size_t offset, std::size_t size) { | void MappedBuffer::Write(const void* src_buffer, std::size_t offset, std::size_t size) const { | ||||||
|     ASSERT(perms & IPC::W); |     ASSERT(perms & IPC::W); | ||||||
|     ASSERT(offset + size <= this->size); |     ASSERT(offset + size <= this->size); | ||||||
|     memory->WriteBlock(*process, address + static_cast<VAddr>(offset), src_buffer, size); |     memory->WriteBlock(*process, address + static_cast<VAddr>(offset), src_buffer, size); | ||||||
|   | |||||||
| @@ -129,14 +129,16 @@ public: | |||||||
|     MappedBuffer(Memory::MemorySystem& memory, std::shared_ptr<Process> process, u32 descriptor, |     MappedBuffer(Memory::MemorySystem& memory, std::shared_ptr<Process> process, u32 descriptor, | ||||||
|                  VAddr address, u32 id); |                  VAddr address, u32 id); | ||||||
|  |  | ||||||
|     // interface for service |     // Interface for service | ||||||
|     void Read(void* dest_buffer, std::size_t offset, std::size_t size); |     void Read(void* dest_buffer, std::size_t offset, std::size_t size) const; | ||||||
|     void Write(const void* src_buffer, std::size_t offset, std::size_t size); |  | ||||||
|  |     void Write(const void* src_buffer, std::size_t offset, std::size_t size) const; | ||||||
|  |  | ||||||
|     std::size_t GetSize() const { |     std::size_t GetSize() const { | ||||||
|         return size; |         return size; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // interface for ipc helper |     // Interface for ipc helper | ||||||
|     u32 GenerateDescriptor() const { |     u32 GenerateDescriptor() const { | ||||||
|         return IPC::MappedBufferDesc(size, perms); |         return IPC::MappedBufferDesc(size, perms); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // Copyright 2015 Citra Emulator Project | // Copyright 2015 Citra Emulator Project | ||||||
| // Licensed under GPLv2 or any later version | // Licensed under GPLv2 or any later version | ||||||
| // Refer to the license.txt file included. | // Refer to the license.txt file included. | ||||||
|  | #pragma optimize("", off) | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <boost/serialization/array.hpp> | #include <boost/serialization/array.hpp> | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
| // Refer to the license.txt file included. | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  | #pragma optimize("", off) | ||||||
| #include <array> | #include <array> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include <type_traits> | #include <type_traits> | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ | |||||||
| #include "core/hle/kernel/shared_memory.h" | #include "core/hle/kernel/shared_memory.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| #include "core/hle/service/soc_u.h" | #include "core/hle/service/soc_u.h" | ||||||
|  | #include "core/network/network.h" | ||||||
|  |  | ||||||
| #ifdef _WIN32 | #ifdef _WIN32 | ||||||
| #include <winsock2.h> | #include <winsock2.h> | ||||||
| @@ -153,112 +154,6 @@ static u32 SocketTypeFromPlatform(u32 type) { | |||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Holds the translation from system network errors to 3DS network errors |  | ||||||
| static const std::unordered_map<int, int> error_map = {{ |  | ||||||
|     {E2BIG, 1}, |  | ||||||
|     {ERRNO(EACCES), 2}, |  | ||||||
|     {ERRNO(EADDRINUSE), 3}, |  | ||||||
|     {ERRNO(EADDRNOTAVAIL), 4}, |  | ||||||
|     {ERRNO(EAFNOSUPPORT), 5}, |  | ||||||
|     {EAGAIN, 6}, |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     {WSAEWOULDBLOCK, 6}, |  | ||||||
| #else |  | ||||||
| #if EAGAIN != EWOULDBLOCK |  | ||||||
|     {EWOULDBLOCK, 6}, |  | ||||||
| #endif |  | ||||||
| #endif // _WIN32 |  | ||||||
|     {ERRNO(EALREADY), 7}, |  | ||||||
|     {ERRNO(EBADF), 8}, |  | ||||||
|     {EBADMSG, 9}, |  | ||||||
|     {EBUSY, 10}, |  | ||||||
|     {ECANCELED, 11}, |  | ||||||
|     {ECHILD, 12}, |  | ||||||
|     {ERRNO(ECONNABORTED), 13}, |  | ||||||
|     {ERRNO(ECONNREFUSED), 14}, |  | ||||||
|     {ERRNO(ECONNRESET), 15}, |  | ||||||
|     {EDEADLK, 16}, |  | ||||||
|     {ERRNO(EDESTADDRREQ), 17}, |  | ||||||
|     {EDOM, 18}, |  | ||||||
|     {ERRNO(EDQUOT), 19}, |  | ||||||
|     {EEXIST, 20}, |  | ||||||
|     {ERRNO(EFAULT), 21}, |  | ||||||
|     {EFBIG, 22}, |  | ||||||
|     {ERRNO(EHOSTUNREACH), 23}, |  | ||||||
|     {EIDRM, 24}, |  | ||||||
|     {EILSEQ, 25}, |  | ||||||
|     {ERRNO(EINPROGRESS), 26}, |  | ||||||
|     {ERRNO(EINTR), 27}, |  | ||||||
|     {ERRNO(EINVAL), 28}, |  | ||||||
|     {EIO, 29}, |  | ||||||
|     {ERRNO(EISCONN), 30}, |  | ||||||
|     {EISDIR, 31}, |  | ||||||
|     {ERRNO(ELOOP), 32}, |  | ||||||
|     {ERRNO(EMFILE), 33}, |  | ||||||
|     {EMLINK, 34}, |  | ||||||
|     {ERRNO(EMSGSIZE), 35}, |  | ||||||
| #ifdef EMULTIHOP |  | ||||||
|     {ERRNO(EMULTIHOP), 36}, |  | ||||||
| #endif |  | ||||||
|     {ERRNO(ENAMETOOLONG), 37}, |  | ||||||
|     {ERRNO(ENETDOWN), 38}, |  | ||||||
|     {ERRNO(ENETRESET), 39}, |  | ||||||
|     {ERRNO(ENETUNREACH), 40}, |  | ||||||
|     {ENFILE, 41}, |  | ||||||
|     {ERRNO(ENOBUFS), 42}, |  | ||||||
| #ifdef ENODATA |  | ||||||
|     {ENODATA, 43}, |  | ||||||
| #endif |  | ||||||
|     {ENODEV, 44}, |  | ||||||
|     {ENOENT, 45}, |  | ||||||
|     {ENOEXEC, 46}, |  | ||||||
|     {ENOLCK, 47}, |  | ||||||
|     {ENOLINK, 48}, |  | ||||||
|     {ENOMEM, 49}, |  | ||||||
|     {ENOMSG, 50}, |  | ||||||
|     {ERRNO(ENOPROTOOPT), 51}, |  | ||||||
|     {ENOSPC, 52}, |  | ||||||
| #ifdef ENOSR |  | ||||||
|     {ENOSR, 53}, |  | ||||||
| #endif |  | ||||||
| #ifdef ENOSTR |  | ||||||
|     {ENOSTR, 54}, |  | ||||||
| #endif |  | ||||||
|     {ENOSYS, 55}, |  | ||||||
|     {ERRNO(ENOTCONN), 56}, |  | ||||||
|     {ENOTDIR, 57}, |  | ||||||
|     {ERRNO(ENOTEMPTY), 58}, |  | ||||||
|     {ERRNO(ENOTSOCK), 59}, |  | ||||||
|     {ENOTSUP, 60}, |  | ||||||
|     {ENOTTY, 61}, |  | ||||||
|     {ENXIO, 62}, |  | ||||||
|     {ERRNO(EOPNOTSUPP), 63}, |  | ||||||
|     {EOVERFLOW, 64}, |  | ||||||
|     {EPERM, 65}, |  | ||||||
|     {EPIPE, 66}, |  | ||||||
|     {EPROTO, 67}, |  | ||||||
|     {ERRNO(EPROTONOSUPPORT), 68}, |  | ||||||
|     {ERRNO(EPROTOTYPE), 69}, |  | ||||||
|     {ERANGE, 70}, |  | ||||||
|     {EROFS, 71}, |  | ||||||
|     {ESPIPE, 72}, |  | ||||||
|     {ESRCH, 73}, |  | ||||||
|     {ERRNO(ESTALE), 74}, |  | ||||||
| #ifdef ETIME |  | ||||||
|     {ETIME, 75}, |  | ||||||
| #endif |  | ||||||
|     {ERRNO(ETIMEDOUT), 76}, |  | ||||||
| }}; |  | ||||||
|  |  | ||||||
| /// Converts a network error from platform-specific to 3ds-specific |  | ||||||
| static int TranslateError(int error) { |  | ||||||
|     const auto& found = error_map.find(error); |  | ||||||
|     if (found != error_map.end()) { |  | ||||||
|         return -found->second; |  | ||||||
|     } |  | ||||||
|     return error; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct CTRLinger { | struct CTRLinger { | ||||||
|     u32_le l_onoff; |     u32_le l_onoff; | ||||||
|     u32_le l_linger; |     u32_le l_linger; | ||||||
| @@ -371,42 +266,6 @@ static void TranslateSockOptDataFromPlatform(std::vector<u8>& out, const std::ve | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| bool SOC_U::GetSocketBlocking(const SocketHolder& socket_holder) { |  | ||||||
|     return socket_holder.blocking; |  | ||||||
| } |  | ||||||
| u32 SOC_U::SetSocketBlocking(SocketHolder& socket_holder, bool blocking) { |  | ||||||
|     u32 posix_ret = 0; |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     unsigned long nonblocking = (blocking) ? 0 : 1; |  | ||||||
|     int ret = ioctlsocket(socket_holder.socket_fd, FIONBIO, &nonblocking); |  | ||||||
|     if (ret == SOCKET_ERROR_VALUE) { |  | ||||||
|         posix_ret = TranslateError(GET_ERRNO); |  | ||||||
|         return posix_ret; |  | ||||||
|     } |  | ||||||
|     socket_holder.blocking = blocking; |  | ||||||
| #else |  | ||||||
|     int flags = ::fcntl(socket_holder.socket_fd, F_GETFL, 0); |  | ||||||
|     if (flags == SOCKET_ERROR_VALUE) { |  | ||||||
|         posix_ret = TranslateError(GET_ERRNO); |  | ||||||
|         return posix_ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     flags &= ~O_NONBLOCK; |  | ||||||
|     if (!blocking) { // O_NONBLOCK |  | ||||||
|         flags |= O_NONBLOCK; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     socket_holder.blocking = blocking; |  | ||||||
|  |  | ||||||
|     const int ret = ::fcntl(socket_holder.socket_fd, F_SETFL, flags); |  | ||||||
|     if (ret == SOCKET_ERROR_VALUE) { |  | ||||||
|         posix_ret = TranslateError(GET_ERRNO); |  | ||||||
|         return posix_ret; |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
|     return posix_ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static u32 SendRecvFlagsToPlatform(u32 flags) { | static u32 SendRecvFlagsToPlatform(u32 flags) { | ||||||
|     u32 ret = 0; |     u32 ret = 0; | ||||||
|     if (flags & 1) { |     if (flags & 1) { | ||||||
| @@ -513,173 +372,10 @@ struct CTRPollFD { | |||||||
|     }; |     }; | ||||||
|     Events events;  ///< Events to poll for (input) |     Events events;  ///< Events to poll for (input) | ||||||
|     Events revents; ///< Events received (output) |     Events revents; ///< Events received (output) | ||||||
|  |  | ||||||
|     /// Converts a platform-specific pollfd to a 3ds specific structure |  | ||||||
|     static CTRPollFD FromPlatform(SOC::SOC_U& socu, pollfd const& fd, u8 has_libctru_bug) { |  | ||||||
|         CTRPollFD result; |  | ||||||
|         result.events.hex = Events::TranslateTo3DS(fd.events, has_libctru_bug).hex; |  | ||||||
|         result.revents.hex = Events::TranslateTo3DS(fd.revents, has_libctru_bug).hex; |  | ||||||
|         for (const auto& socket : socu.open_sockets) { |  | ||||||
|             if (socket.second.socket_fd == fd.fd) { |  | ||||||
|                 result.fd = socket.first; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Converts a 3ds specific pollfd to a platform-specific structure |  | ||||||
|     static pollfd ToPlatform(SOC::SOC_U& socu, CTRPollFD const& fd, u8& haslibctrbug) { |  | ||||||
|         pollfd result; |  | ||||||
|         u8 unused = 0; |  | ||||||
|         result.events = Events::TranslateToPlatform(fd.events, false, haslibctrbug); |  | ||||||
|         result.revents = Events::TranslateToPlatform(fd.revents, true, unused); |  | ||||||
|         auto iter = socu.open_sockets.find(fd.fd); |  | ||||||
|         result.fd = (iter != socu.open_sockets.end()) ? iter->second.socket_fd : 0; |  | ||||||
|         if (iter == socu.open_sockets.end()) { |  | ||||||
|             LOG_ERROR(Service_SOC, "Invalid socket handle: {}", fd.fd); |  | ||||||
|         } |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
| }; | }; | ||||||
| static_assert(std::is_trivially_copyable_v<CTRPollFD>, | static_assert(std::is_trivially_copyable_v<CTRPollFD>, | ||||||
|               "CTRPollFD is used with std::memcpy and must be trivially copyable"); |               "CTRPollFD is used with std::memcpy and must be trivially copyable"); | ||||||
|  |  | ||||||
| /// Union to represent the 3ds' sockaddr structure |  | ||||||
| union CTRSockAddr { |  | ||||||
|     /// Structure to represent a raw sockaddr |  | ||||||
|     struct { |  | ||||||
|         u8 len;           ///< The length of the entire structure, only the set fields count |  | ||||||
|         u8 sa_family;     ///< The address family of the sockaddr |  | ||||||
|         u8 sa_data[0x1A]; ///< The extra data, this varies, depending on the address family |  | ||||||
|     } raw; |  | ||||||
|  |  | ||||||
|     /// Structure to represent the 3ds' sockaddr_in structure |  | ||||||
|     struct CTRSockAddrIn { |  | ||||||
|         u8 len;        ///< The length of the entire structure |  | ||||||
|         u8 sin_family; ///< The address family of the sockaddr_in |  | ||||||
|         u16 sin_port;  ///< The port associated with this sockaddr_in |  | ||||||
|         u32 sin_addr;  ///< The actual address of the sockaddr_in |  | ||||||
|     } in; |  | ||||||
|     static_assert(sizeof(CTRSockAddrIn) == 8, "Invalid CTRSockAddrIn size"); |  | ||||||
|  |  | ||||||
|     /// Convert a 3DS CTRSockAddr to a platform-specific sockaddr |  | ||||||
|     static sockaddr ToPlatform(CTRSockAddr const& ctr_addr) { |  | ||||||
|         sockaddr result; |  | ||||||
|         ASSERT_MSG(ctr_addr.raw.len == sizeof(CTRSockAddrIn), |  | ||||||
|                    "Unhandled address size (len) in CTRSockAddr::ToPlatform"); |  | ||||||
|         result.sa_family = SocketDomainToPlatform(ctr_addr.raw.sa_family); |  | ||||||
|         memset(result.sa_data, 0, sizeof(result.sa_data)); |  | ||||||
|  |  | ||||||
|         // We can not guarantee ABI compatibility between platforms so we copy the fields manually |  | ||||||
|         switch (result.sa_family) { |  | ||||||
|         case AF_INET: { |  | ||||||
|             sockaddr_in* result_in = reinterpret_cast<sockaddr_in*>(&result); |  | ||||||
|             result_in->sin_port = ctr_addr.in.sin_port; |  | ||||||
|             result_in->sin_addr.s_addr = ctr_addr.in.sin_addr; |  | ||||||
|             memset(result_in->sin_zero, 0, sizeof(result_in->sin_zero)); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         default: |  | ||||||
|             ASSERT_MSG(false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform"); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Convert a platform-specific sockaddr to a 3DS CTRSockAddr |  | ||||||
|     static CTRSockAddr FromPlatform(sockaddr const& addr) { |  | ||||||
|         CTRSockAddr result; |  | ||||||
|         result.raw.sa_family = static_cast<u8>(SocketDomainFromPlatform(addr.sa_family)); |  | ||||||
|         // We can not guarantee ABI compatibility between platforms so we copy the fields manually |  | ||||||
|         switch (addr.sa_family) { |  | ||||||
|         case AF_INET: { |  | ||||||
|             sockaddr_in const* addr_in = reinterpret_cast<sockaddr_in const*>(&addr); |  | ||||||
|             result.raw.len = sizeof(CTRSockAddrIn); |  | ||||||
|             result.in.sin_port = addr_in->sin_port; |  | ||||||
|             result.in.sin_addr = addr_in->sin_addr.s_addr; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         default: |  | ||||||
|             ASSERT_MSG(false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform"); |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         return result; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct CTRAddrInfo { |  | ||||||
|     s32_le ai_flags; |  | ||||||
|     s32_le ai_family; |  | ||||||
|     s32_le ai_socktype; |  | ||||||
|     s32_le ai_protocol; |  | ||||||
|     s32_le ai_addrlen; |  | ||||||
|     char ai_canonname[256]; |  | ||||||
|     CTRSockAddr ai_addr; |  | ||||||
|  |  | ||||||
|     static u32 AddressInfoFlagsToPlatform(u32 flags) { |  | ||||||
|         u32 ret = 0; |  | ||||||
|         if (flags & 1) { |  | ||||||
|             ret |= AI_PASSIVE; |  | ||||||
|         } |  | ||||||
|         if (flags & 2) { |  | ||||||
|             ret |= AI_CANONNAME; |  | ||||||
|         } |  | ||||||
|         if (flags & 4) { |  | ||||||
|             ret |= AI_NUMERICHOST; |  | ||||||
|         } |  | ||||||
|         if (flags & 8) { |  | ||||||
|             ret |= AI_NUMERICSERV; |  | ||||||
|         } |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     static u32 AddressInfoFlagsFromPlatform(u32 flags) { |  | ||||||
|         u32 ret = 0; |  | ||||||
|         if (flags & AI_PASSIVE) { |  | ||||||
|             ret |= 1; |  | ||||||
|         } |  | ||||||
|         if (flags & AI_CANONNAME) { |  | ||||||
|             ret |= 2; |  | ||||||
|         } |  | ||||||
|         if (flags & AI_NUMERICHOST) { |  | ||||||
|             ret |= 4; |  | ||||||
|         } |  | ||||||
|         if (flags & AI_NUMERICSERV) { |  | ||||||
|             ret |= 8; |  | ||||||
|         } |  | ||||||
|         return ret; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Converts a platform-specific addrinfo to a 3ds addrinfo. |  | ||||||
|     static CTRAddrInfo FromPlatform(const addrinfo& addr) { |  | ||||||
|         CTRAddrInfo ctr_addr{ |  | ||||||
|             .ai_flags = static_cast<s32_le>(AddressInfoFlagsFromPlatform(addr.ai_flags)), |  | ||||||
|             .ai_family = static_cast<s32_le>(SocketDomainFromPlatform(addr.ai_family)), |  | ||||||
|             .ai_socktype = static_cast<s32_le>(SocketTypeFromPlatform(addr.ai_socktype)), |  | ||||||
|             .ai_protocol = static_cast<s32_le>(SocketProtocolFromPlatform(addr.ai_protocol)), |  | ||||||
|             .ai_addr = CTRSockAddr::FromPlatform(*addr.ai_addr), |  | ||||||
|         }; |  | ||||||
|         ctr_addr.ai_addrlen = static_cast<s32_le>(ctr_addr.ai_addr.raw.len); |  | ||||||
|         if (addr.ai_canonname) |  | ||||||
|             std::strncpy(ctr_addr.ai_canonname, addr.ai_canonname, |  | ||||||
|                          sizeof(ctr_addr.ai_canonname) - 1); |  | ||||||
|         return ctr_addr; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Converts a platform-specific addrinfo to a 3ds addrinfo. |  | ||||||
|     static addrinfo ToPlatform(const CTRAddrInfo& ctr_addr) { |  | ||||||
|         // Only certain fields are meaningful in hints, copy them manually |  | ||||||
|         addrinfo addr = { |  | ||||||
|             .ai_flags = static_cast<int>(AddressInfoFlagsToPlatform(ctr_addr.ai_flags)), |  | ||||||
|             .ai_family = static_cast<int>(SocketDomainToPlatform(ctr_addr.ai_family)), |  | ||||||
|             .ai_socktype = static_cast<int>(SocketTypeToPlatform(ctr_addr.ai_socktype)), |  | ||||||
|             .ai_protocol = static_cast<int>(SocketProtocolToPlatform(ctr_addr.ai_protocol)), |  | ||||||
|         }; |  | ||||||
|         return addr; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static u32 NameInfoFlagsToPlatform(u32 flags) { | static u32 NameInfoFlagsToPlatform(u32 flags) { | ||||||
|     u32 ret = 0; |     u32 ret = 0; | ||||||
|     if (flags & 1) { |     if (flags & 1) { | ||||||
| @@ -700,8 +396,6 @@ static u32 NameInfoFlagsToPlatform(u32 flags) { | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| static_assert(sizeof(CTRAddrInfo) == 0x130, "Size of CTRAddrInfo is not correct"); |  | ||||||
|  |  | ||||||
| void SOC_U::PreTimerAdjust() { | void SOC_U::PreTimerAdjust() { | ||||||
|     adjust_value_last = std::chrono::steady_clock::now(); |     adjust_value_last = std::chrono::steady_clock::now(); | ||||||
| } | } | ||||||
| @@ -715,84 +409,64 @@ void SOC_U::PostTimerAdjust(Kernel::HLERequestContext& ctx, const std::string& c | |||||||
|         nullptr); |         nullptr); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SOC_U::CleanupSockets() { |  | ||||||
|     for (const auto& sock : open_sockets) |  | ||||||
|         closesocket(sock.second.socket_fd); |  | ||||||
|     open_sockets.clear(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SOC_U::Socket(Kernel::HLERequestContext& ctx) { | void SOC_U::Socket(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x02, 3, 2); |     IPC::RequestParser rp(ctx, 0x02, 3, 2); | ||||||
|     u32 domain = SocketDomainToPlatform(rp.Pop<u32>()); // Address family |     const auto domain = rp.Pop<Network::Domain>(); | ||||||
|     u32 type = SocketTypeToPlatform(rp.Pop<u32>()); |     const auto type = rp.Pop<Network::Type>(); | ||||||
|     u32 protocol = SocketProtocolToPlatform(rp.Pop<u32>()); |     const u32 protocol = rp.Pop<u32>(); | ||||||
|     rp.PopPID(); |     rp.PopPID(); | ||||||
|  |  | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|  |  | ||||||
|     // Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use |     // Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use | ||||||
|     if (protocol != 0) { |     if (protocol != 0) [[unlikely]] { | ||||||
|         rb.Push(UnimplementedFunction(ErrorModule::SOC)); // TODO(Subv): Correct error code |         rb.Push(UnimplementedFunction(ErrorModule::SOC)); // TODO(Subv): Correct error code | ||||||
|         rb.Skip(1, false); |         rb.Skip(1, false); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (domain != AF_INET) { |     if (domain != Network::Domain::INET) [[unlikely]] { | ||||||
|         rb.Push(UnimplementedFunction(ErrorModule::SOC)); // TODO(Subv): Correct error code |         rb.Push(UnimplementedFunction(ErrorModule::SOC)); // TODO(Subv): Correct error code | ||||||
|         rb.Skip(1, false); |         rb.Skip(1, false); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (type != SOCK_DGRAM && type != SOCK_STREAM) { |     if (type != Network::Type::DGRAM && type != Network::Type::STREAM) [[unlikely]] { | ||||||
|         rb.Push(UnimplementedFunction(ErrorModule::SOC)); // TODO(Subv): Correct error code |         rb.Push(UnimplementedFunction(ErrorModule::SOC)); // TODO(Subv): Correct error code | ||||||
|         rb.Skip(1, false); |         rb.Skip(1, false); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     u64 ret = static_cast<u64>(::socket(domain, type, protocol)); |     auto socket = std::make_unique<Network::Socket>(); | ||||||
|     u32 socketHandle = GetNextSocketID(); |     const auto ret = socket->Initialize(domain, type, protocol); | ||||||
|  |     const u32 socket_handle = GetNextSocketID(); | ||||||
|  |  | ||||||
|     if ((s64)ret != SOCKET_ERROR_VALUE) { |     if (ret == Network::Errno::SUCCESS) { | ||||||
|         open_sockets[socketHandle] = {static_cast<decltype(SocketHolder::socket_fd)>(ret), true}; |         open_sockets.emplace(socket_handle, std::move(socket)); | ||||||
| #if _WIN32 |  | ||||||
|         // Disable UDP connection reset |  | ||||||
|         int new_behavior = 0; |  | ||||||
|         unsigned long bytes_returned = 0; |  | ||||||
|         WSAIoctl(static_cast<SOCKET>(ret), _WSAIOW(IOC_VENDOR, 12), &new_behavior, |  | ||||||
|                  sizeof(new_behavior), NULL, 0, &bytes_returned, NULL, NULL); |  | ||||||
| #endif |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ((s64)ret == SOCKET_ERROR_VALUE) |  | ||||||
|         ret = TranslateError(GET_ERRNO); |  | ||||||
|  |  | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.Push(socketHandle); |     rb.Push(socket_handle); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SOC_U::Bind(Kernel::HLERequestContext& ctx) { | void SOC_U::Bind(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x05, 2, 4); |     IPC::RequestParser rp(ctx, 0x05, 2, 4); | ||||||
|     u32 socket_handle = rp.Pop<u32>(); |     const u32 socket_handle = rp.Pop<u32>(); | ||||||
|     auto fd_info = open_sockets.find(socket_handle); |     const u32 len = rp.Pop<u32>(); | ||||||
|     if (fd_info == open_sockets.end()) { |     rp.PopPID(); | ||||||
|         LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); |     const auto sock_addr_buf = rp.PopStaticBuffer(); | ||||||
|  |  | ||||||
|  |     auto socket = FindSocket(socket_handle); | ||||||
|  |     if (!socket) { | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|         rb.Push(ERR_INVALID_HANDLE); |         rb.Push(ERR_INVALID_HANDLE); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     u32 len = rp.Pop<u32>(); |  | ||||||
|     rp.PopPID(); |  | ||||||
|     auto sock_addr_buf = rp.PopStaticBuffer(); |  | ||||||
|  |  | ||||||
|     CTRSockAddr ctr_sock_addr; |     Network::CTRSockAddr ctr_sock_addr{}; | ||||||
|     std::memcpy(&ctr_sock_addr, sock_addr_buf.data(), len); |     std::memcpy(&ctr_sock_addr, sock_addr_buf.data(), len); | ||||||
|  |  | ||||||
|     sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr); |     const auto ret = socket->Bind(ctr_sock_addr); | ||||||
|  |  | ||||||
|     s32 ret = ::bind(fd_info->second.socket_fd, &sock_addr, sizeof(sock_addr)); |  | ||||||
|  |  | ||||||
|     if (ret != 0) |  | ||||||
|         ret = TranslateError(GET_ERRNO); |  | ||||||
|  |  | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
| @@ -801,92 +475,87 @@ void SOC_U::Bind(Kernel::HLERequestContext& ctx) { | |||||||
|  |  | ||||||
| void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) { | void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x13, 3, 2); |     IPC::RequestParser rp(ctx, 0x13, 3, 2); | ||||||
|     u32 socket_handle = rp.Pop<u32>(); |     const u32 socket_handle = rp.Pop<u32>(); | ||||||
|     auto fd_info = open_sockets.find(socket_handle); |     const auto ctr_cmd = rp.Pop<Network::FcntlCmd>(); | ||||||
|     if (fd_info == open_sockets.end()) { |     const u32 ctr_arg = rp.Pop<u32>(); | ||||||
|         LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); |     rp.PopPID(); | ||||||
|  |  | ||||||
|  |     auto socket = FindSocket(socket_handle); | ||||||
|  |     if (!socket) { | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|         rb.Push(ERR_INVALID_HANDLE); |         rb.Push(ERR_INVALID_HANDLE); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     u32 ctr_cmd = rp.Pop<u32>(); |  | ||||||
|     u32 ctr_arg = rp.Pop<u32>(); |  | ||||||
|     rp.PopPID(); |  | ||||||
|  |  | ||||||
|     u32 posix_ret = 0; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX) |     // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX) | ||||||
|     SCOPE_EXIT({ |     u32 posix_ret = 0; | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |     switch (ctr_cmd) { | ||||||
|         rb.Push(RESULT_SUCCESS); |     case Network::FcntlCmd::GETFL: | ||||||
|         rb.Push(posix_ret); |         if (!socket->IsBlocking()) { | ||||||
|     }); |             posix_ret |= 4; // O_NONBLOCK | ||||||
|  |         } | ||||||
|     if (ctr_cmd == 3) { // F_GETFL |         break; | ||||||
|         posix_ret = 0; |     case Network::FcntlCmd::SETFL: | ||||||
|         if (GetSocketBlocking(fd_info->second) == false) |         posix_ret = static_cast<u32>(socket->SetNonBlock(ctr_arg & 4)); | ||||||
|             posix_ret |= 4;    // O_NONBLOCK |     default: | ||||||
|     } else if (ctr_cmd == 4) { // F_SETFL |  | ||||||
|         posix_ret = SetSocketBlocking(fd_info->second, !(ctr_arg & 4)); |  | ||||||
|     } else { |  | ||||||
|         LOG_ERROR(Service_SOC, "Unsupported command ({}) in fcntl call", ctr_cmd); |         LOG_ERROR(Service_SOC, "Unsupported command ({}) in fcntl call", ctr_cmd); | ||||||
|         posix_ret = TranslateError(EINVAL); // TODO: Find the correct error |         posix_ret = static_cast<u32>(Network::Errno::INVAL); // TODO: Find the correct error | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|  |     rb.Push(RESULT_SUCCESS); | ||||||
|  |     rb.Push(posix_ret); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SOC_U::Listen(Kernel::HLERequestContext& ctx) { | void SOC_U::Listen(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x03, 2, 2); |     IPC::RequestParser rp(ctx, 0x03, 2, 2); | ||||||
|     u32 socket_handle = rp.Pop<u32>(); |     const u32 socket_handle = rp.Pop<u32>(); | ||||||
|     auto fd_info = open_sockets.find(socket_handle); |     const u32 backlog = rp.Pop<u32>(); | ||||||
|     if (fd_info == open_sockets.end()) { |     rp.PopPID(); | ||||||
|         LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); |  | ||||||
|  |     auto socket = FindSocket(socket_handle); | ||||||
|  |     if (!socket) { | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|         rb.Push(ERR_INVALID_HANDLE); |         rb.Push(ERR_INVALID_HANDLE); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     u32 backlog = rp.Pop<u32>(); |  | ||||||
|     rp.PopPID(); |  | ||||||
|  |  | ||||||
|     s32 ret = ::listen(fd_info->second.socket_fd, backlog); |     const auto ret = socket->Listen(backlog); | ||||||
|     if (ret != 0) |  | ||||||
|         ret = TranslateError(GET_ERRNO); |  | ||||||
|  |  | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.Push(ret); |     rb.Push(ret); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static_assert(sizeof(socklen_t) == sizeof(u32), "socklen_t has incorrect size!"); | ||||||
|  |  | ||||||
| void SOC_U::Accept(Kernel::HLERequestContext& ctx) { | void SOC_U::Accept(Kernel::HLERequestContext& ctx) { | ||||||
|     // TODO(Subv): Calling this function on a blocking socket will block the emu thread, |     // TODO(Subv): Calling this function on a blocking socket will block the emu thread, | ||||||
|     // preventing graceful shutdown when closing the emulator, this can be fixed by always |     // preventing graceful shutdown when closing the emulator, this can be fixed by always | ||||||
|     // performing nonblocking operations and spinlock until the data is available |     // performing nonblocking operations and spinlock until the data is available | ||||||
|     IPC::RequestParser rp(ctx, 0x04, 2, 2); |     IPC::RequestParser rp(ctx, 0x04, 2, 2); | ||||||
|     const auto socket_handle = rp.Pop<u32>(); |     const auto socket_handle = rp.Pop<u32>(); | ||||||
|     auto fd_info = open_sockets.find(socket_handle); |     [[maybe_unused]] const auto max_addr_len = rp.Pop<socklen_t>(); | ||||||
|     if (fd_info == open_sockets.end()) { |     rp.PopPID(); | ||||||
|         LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); |  | ||||||
|  |     auto socket = FindSocket(socket_handle); | ||||||
|  |     if (!socket) { | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|         rb.Push(ERR_INVALID_HANDLE); |         rb.Push(ERR_INVALID_HANDLE); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     [[maybe_unused]] const auto max_addr_len = static_cast<socklen_t>(rp.Pop<u32>()); |  | ||||||
|     rp.PopPID(); |  | ||||||
|     sockaddr addr; |  | ||||||
|     socklen_t addr_len = sizeof(addr); |  | ||||||
|     u32 ret = static_cast<u32>(::accept(fd_info->second.socket_fd, &addr, &addr_len)); |  | ||||||
|  |  | ||||||
|     if (static_cast<s32>(ret) != SOCKET_ERROR_VALUE) { |     auto [result, err] = socket->Accept(); | ||||||
|         u32 socketID = GetNextSocketID(); |  | ||||||
|         open_sockets[socketID] = {static_cast<decltype(SocketHolder::socket_fd)>(ret), true}; |  | ||||||
|         ret = socketID; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     CTRSockAddr ctr_addr; |     constexpr u32 size = sizeof(result.sockaddr_in); | ||||||
|     std::vector<u8> ctr_addr_buf(sizeof(ctr_addr)); |     std::vector<u8> ctr_addr_buf(size); | ||||||
|     if (static_cast<s32>(ret) == SOCKET_ERROR_VALUE) { |  | ||||||
|         ret = TranslateError(GET_ERRNO); |     u32 ret = static_cast<u32>(err); | ||||||
|     } else { |     if (err == Network::Errno::SUCCESS) { | ||||||
|         ctr_addr = CTRSockAddr::FromPlatform(addr); |         const u32 socket_id = GetNextSocketID(); | ||||||
|         std::memcpy(ctr_addr_buf.data(), &ctr_addr, sizeof(ctr_addr)); |         open_sockets.emplace(socket_id, std::move(result.socket)); | ||||||
|  |         std::memcpy(ctr_addr_buf.data(), &result.sockaddr_in, size); | ||||||
|  |         ret = socket_id; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | ||||||
| @@ -899,7 +568,7 @@ void SOC_U::GetHostId(Kernel::HLERequestContext& ctx) { | |||||||
|     IPC::RequestParser rp(ctx, 0x16, 0, 0); |     IPC::RequestParser rp(ctx, 0x16, 0, 0); | ||||||
|  |  | ||||||
|     u32 host_id = 0; |     u32 host_id = 0; | ||||||
|     auto info = GetDefaultInterfaceInfo(); |     const auto info = GetDefaultInterfaceInfo(); | ||||||
|     if (info.has_value()) { |     if (info.has_value()) { | ||||||
|         host_id = info->address; |         host_id = info->address; | ||||||
|     } |     } | ||||||
| @@ -911,25 +580,19 @@ void SOC_U::GetHostId(Kernel::HLERequestContext& ctx) { | |||||||
|  |  | ||||||
| void SOC_U::Close(Kernel::HLERequestContext& ctx) { | void SOC_U::Close(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x0B, 1, 2); |     IPC::RequestParser rp(ctx, 0x0B, 1, 2); | ||||||
|     u32 socket_handle = rp.Pop<u32>(); |     const u32 socket_handle = rp.Pop<u32>(); | ||||||
|     auto fd_info = open_sockets.find(socket_handle); |     rp.PopPID(); | ||||||
|     if (fd_info == open_sockets.end()) { |  | ||||||
|         LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); |     auto socket = FindSocket(socket_handle); | ||||||
|  |     if (!socket) { | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|         rb.Push(ERR_INVALID_HANDLE); |         rb.Push(ERR_INVALID_HANDLE); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     rp.PopPID(); |  | ||||||
|  |  | ||||||
|     s32 ret = 0; |  | ||||||
|  |  | ||||||
|     ret = closesocket(fd_info->second.socket_fd); |  | ||||||
|  |  | ||||||
|  |     const auto ret = socket->Close(); | ||||||
|     open_sockets.erase(socket_handle); |     open_sockets.erase(socket_handle); | ||||||
|  |  | ||||||
|     if (ret != 0) |  | ||||||
|         ret = TranslateError(GET_ERRNO); |  | ||||||
|  |  | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.Push(ret); |     rb.Push(ret); | ||||||
| @@ -938,59 +601,48 @@ void SOC_U::Close(Kernel::HLERequestContext& ctx) { | |||||||
| void SOC_U::SendToOther(Kernel::HLERequestContext& ctx) { | void SOC_U::SendToOther(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x09, 4, 6); |     IPC::RequestParser rp(ctx, 0x09, 4, 6); | ||||||
|     const u32 socket_handle = rp.Pop<u32>(); |     const u32 socket_handle = rp.Pop<u32>(); | ||||||
|     const auto fd_info = open_sockets.find(socket_handle); |     const u32 len = rp.Pop<u32>(); | ||||||
|     if (fd_info == open_sockets.end()) { |     const auto flags = rp.Pop<Network::SendFlags>(); | ||||||
|         LOG_ERROR(Service_SOC, "Invalid socket handle: {}", socket_handle); |     const u32 addr_len = rp.Pop<u32>(); | ||||||
|  |     rp.PopPID(); | ||||||
|  |     const auto dest_addr_buffer = rp.PopStaticBuffer(); | ||||||
|  |     const auto input_mapped_buff = rp.PopMappedBuffer(); | ||||||
|  |  | ||||||
|  |     auto socket = FindSocket(socket_handle); | ||||||
|  |     if (!socket) { | ||||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|         rb.Push(ERR_INVALID_HANDLE); |         rb.Push(ERR_INVALID_HANDLE); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     const u32 len = rp.Pop<u32>(); |  | ||||||
|     u32 flags = SendRecvFlagsToPlatform(rp.Pop<u32>()); |  | ||||||
|     bool dont_wait = (flags & MSGCUSTOM_HANDLE_DONTWAIT) != 0; |  | ||||||
|     flags &= ~MSGCUSTOM_HANDLE_DONTWAIT; |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     bool was_blocking = GetSocketBlocking(fd_info->second); |  | ||||||
|     if (dont_wait && was_blocking) { |  | ||||||
|         SetSocketBlocking(fd_info->second, false); |  | ||||||
|     } |  | ||||||
| #else |  | ||||||
|     if (dont_wait) { |  | ||||||
|         flags |= MSG_DONTWAIT; |  | ||||||
|     } |  | ||||||
| #endif // _WIN32 |  | ||||||
|     const u32 addr_len = rp.Pop<u32>(); |  | ||||||
|     rp.PopPID(); |  | ||||||
|     const auto dest_addr_buffer = rp.PopStaticBuffer(); |  | ||||||
|  |  | ||||||
|     auto input_mapped_buff = rp.PopMappedBuffer(); | #if WIN32 | ||||||
|     std::vector<u8> input_buff(len); |     const bool unblock = True(flags & Network::SendFlags::DONTWAIT) && socket->IsBlocking(); | ||||||
|     input_mapped_buff.Read(input_buff.data(), 0, |     if (unblock) { | ||||||
|                            std::min(input_mapped_buff.GetSize(), static_cast<size_t>(len))); |         socket->SetNonBlock(true); | ||||||
|  |  | ||||||
|     s32 ret = -1; |  | ||||||
|     if (addr_len > 0) { |  | ||||||
|         CTRSockAddr ctr_dest_addr; |  | ||||||
|         std::memcpy(&ctr_dest_addr, dest_addr_buffer.data(), sizeof(ctr_dest_addr)); |  | ||||||
|         sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr); |  | ||||||
|         ret = ::sendto(fd_info->second.socket_fd, reinterpret_cast<const char*>(input_buff.data()), |  | ||||||
|                        len, flags, &dest_addr, sizeof(dest_addr)); |  | ||||||
|     } else { |  | ||||||
|         ret = ::sendto(fd_info->second.socket_fd, reinterpret_cast<const char*>(input_buff.data()), |  | ||||||
|                        len, flags, nullptr, 0); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const auto send_error = (ret == SOCKET_ERROR_VALUE) ? GET_ERRNO : 0; |  | ||||||
|  |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     if (dont_wait && was_blocking) { |  | ||||||
|         SetSocketBlocking(fd_info->second, true); |  | ||||||
|     } |     } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     if (ret == SOCKET_ERROR_VALUE) { |     std::vector<u8> message(len); | ||||||
|         ret = TranslateError(send_error); |     const size_t size = std::min<size_t>(input_mapped_buff.GetSize(), len); | ||||||
|  |     input_mapped_buff.Read(message.data(), 0, size); | ||||||
|  |  | ||||||
|  |     const auto [result, err] = [&] { | ||||||
|  |         if (addr_len > 0) { | ||||||
|  |             Network::CTRSockAddr ctr_dest_addr; | ||||||
|  |             std::memcpy(&ctr_dest_addr, dest_addr_buffer.data(), sizeof(ctr_dest_addr)); | ||||||
|  |             return socket->SendTo(flags, message, &ctr_dest_addr); | ||||||
|  |         } else { | ||||||
|  |             return socket->SendTo(flags, message, nullptr); | ||||||
|  |         } | ||||||
|  |     }(); | ||||||
|  |  | ||||||
|  |     const u32 ret = err != Network::Errno::SUCCESS ? static_cast<u32>(err) : result; | ||||||
|  |  | ||||||
|  | #if WIN32 | ||||||
|  |     if (unblock) { | ||||||
|  |         socket->SetNonBlock(false); | ||||||
|     } |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
| @@ -1430,7 +1082,7 @@ void SOC_U::InitializeSockets(Kernel::HLERequestContext& ctx) { | |||||||
| void SOC_U::ShutdownSockets(Kernel::HLERequestContext& ctx) { | void SOC_U::ShutdownSockets(Kernel::HLERequestContext& ctx) { | ||||||
|     // TODO(Subv): Implement |     // TODO(Subv): Implement | ||||||
|     IPC::RequestParser rp(ctx, 0x19, 0, 0); |     IPC::RequestParser rp(ctx, 0x19, 0, 0); | ||||||
|     CleanupSockets(); |     open_sockets.clear(); | ||||||
|  |  | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
| @@ -1674,6 +1326,15 @@ void SOC_U::GetNameInfoImpl(Kernel::HLERequestContext& ctx) { | |||||||
|     rb.PushStaticBuffer(std::move(serv), 1); |     rb.PushStaticBuffer(std::move(serv), 1); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | Network::Socket* SOC_U::FindSocket(u32 handle) { | ||||||
|  |     const auto it = open_sockets.find(handle); | ||||||
|  |     if (it == open_sockets.end()) { | ||||||
|  |         LOG_ERROR(Service_SOC, "Invalid socket handle: {}", handle); | ||||||
|  |         return nullptr; | ||||||
|  |     } | ||||||
|  |     return it->second.get(); | ||||||
|  | } | ||||||
|  |  | ||||||
| SOC_U::SOC_U() : ServiceFramework("soc:U") { | SOC_U::SOC_U() : ServiceFramework("soc:U") { | ||||||
|     static const FunctionInfo functions[] = { |     static const FunctionInfo functions[] = { | ||||||
|         // clang-format off |         // clang-format off | ||||||
| @@ -1714,19 +1375,9 @@ SOC_U::SOC_U() : ServiceFramework("soc:U") { | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     RegisterHandlers(functions); |     RegisterHandlers(functions); | ||||||
|  |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     WSADATA data; |  | ||||||
|     WSAStartup(MAKEWORD(2, 2), &data); |  | ||||||
| #endif |  | ||||||
| } | } | ||||||
|  |  | ||||||
| SOC_U::~SOC_U() { | SOC_U::~SOC_U() = default; | ||||||
|     CleanupSockets(); |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     WSACleanup(); |  | ||||||
| #endif |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::optional<SOC_U::InterfaceInfo> SOC_U::GetDefaultInterfaceInfo() { | std::optional<SOC_U::InterfaceInfo> SOC_U::GetDefaultInterfaceInfo() { | ||||||
|     if (this->interface_info_cached) { |     if (this->interface_info_cached) { | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ | |||||||
| #include <boost/serialization/unordered_map.hpp> | #include <boost/serialization/unordered_map.hpp> | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
|  | #include "core/network/sockets.h" | ||||||
|  |  | ||||||
| namespace Core { | namespace Core { | ||||||
| class System; | class System; | ||||||
| @@ -16,26 +17,6 @@ class System; | |||||||
|  |  | ||||||
| namespace Service::SOC { | namespace Service::SOC { | ||||||
|  |  | ||||||
| /// Holds information about a particular socket |  | ||||||
| struct SocketHolder { |  | ||||||
| #ifdef _WIN32 |  | ||||||
|     using SOCKET = unsigned long long; |  | ||||||
|     SOCKET socket_fd; ///< The socket descriptor |  | ||||||
| #else |  | ||||||
|     int socket_fd; ///< The socket descriptor |  | ||||||
| #endif // _WIN32 |  | ||||||
|  |  | ||||||
|     bool blocking = true; ///< Whether the socket is blocking or not. |  | ||||||
|  |  | ||||||
| private: |  | ||||||
|     template <class Archive> |  | ||||||
|     void serialize(Archive& ar, const unsigned int) { |  | ||||||
|         ar& socket_fd; |  | ||||||
|         ar& blocking; |  | ||||||
|     } |  | ||||||
|     friend class boost::serialization::access; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| class SOC_U final : public ServiceFramework<SOC_U> { | class SOC_U final : public ServiceFramework<SOC_U> { | ||||||
| public: | public: | ||||||
|     SOC_U(); |     SOC_U(); | ||||||
| @@ -63,11 +44,8 @@ private: | |||||||
|  |  | ||||||
|     static const std::unordered_map<u64, std::pair<int, int>> sockopt_map; |     static const std::unordered_map<u64, std::pair<int, int>> sockopt_map; | ||||||
|     static std::pair<int, int> TranslateSockOpt(int level, int opt); |     static std::pair<int, int> TranslateSockOpt(int level, int opt); | ||||||
|     bool GetSocketBlocking(const SocketHolder& socket_holder); |  | ||||||
|     u32 SetSocketBlocking(SocketHolder& socket_holder, bool blocking); |  | ||||||
|  |  | ||||||
|     // From |     // From https://github.com/devkitPro/libctru/blob/1de86ea3/libctru/include/3ds/services/soc.h#L15 | ||||||
|     // https://github.com/devkitPro/libctru/blob/1de86ea38aec419744149daf692556e187d4678a/libctru/include/3ds/services/soc.h#L15 |  | ||||||
|     enum class NetworkOpt { |     enum class NetworkOpt { | ||||||
|         NETOPT_MAC_ADDRESS = 0x1004,     ///< The mac address of the interface |         NETOPT_MAC_ADDRESS = 0x1004,     ///< The mac address of the interface | ||||||
|         NETOPT_ARP_TABLE = 0x3002,       ///< The ARP table |         NETOPT_ARP_TABLE = 0x3002,       ///< The ARP table | ||||||
| @@ -123,30 +101,14 @@ private: | |||||||
|     void GetAddrInfoImpl(Kernel::HLERequestContext& ctx); |     void GetAddrInfoImpl(Kernel::HLERequestContext& ctx); | ||||||
|     void GetNameInfoImpl(Kernel::HLERequestContext& ctx); |     void GetNameInfoImpl(Kernel::HLERequestContext& ctx); | ||||||
|  |  | ||||||
|     // Socked ids |     Network::Socket* FindSocket(u32 handle); | ||||||
|     u32 next_socket_id = 3; |  | ||||||
|     u32 GetNextSocketID() { |  | ||||||
|         return next_socket_id++; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // System timer adjust |  | ||||||
|     std::chrono::time_point<std::chrono::steady_clock> adjust_value_last; |  | ||||||
|     void PreTimerAdjust(); |     void PreTimerAdjust(); | ||||||
|     void PostTimerAdjust(Kernel::HLERequestContext& ctx, const std::string& caller_method); |     void PostTimerAdjust(Kernel::HLERequestContext& ctx, const std::string& caller_method); | ||||||
|  |  | ||||||
|     /// Close all open sockets |     [[nodiscard]] u32 GetNextSocketID() { | ||||||
|     void CleanupSockets(); |         return next_socket_id++; | ||||||
|  |     } | ||||||
|     /// Holds info about the currently open sockets |  | ||||||
|     friend struct CTRPollFD; |  | ||||||
|     std::unordered_map<u32, SocketHolder> open_sockets; |  | ||||||
|  |  | ||||||
|     /// Cache interface info for the current session |  | ||||||
|     /// These two fields are not saved to savestates on purpose |  | ||||||
|     /// as network interfaces may change and it's better to. |  | ||||||
|     /// obtain them again between play sessions. |  | ||||||
|     bool interface_info_cached = false; |  | ||||||
|     InterfaceInfo interface_info; |  | ||||||
|  |  | ||||||
|     template <class Archive> |     template <class Archive> | ||||||
|     void serialize(Archive& ar, const unsigned int) { |     void serialize(Archive& ar, const unsigned int) { | ||||||
| @@ -154,6 +116,18 @@ private: | |||||||
|         ar& open_sockets; |         ar& open_sockets; | ||||||
|     } |     } | ||||||
|     friend class boost::serialization::access; |     friend class boost::serialization::access; | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     std::chrono::time_point<std::chrono::steady_clock> adjust_value_last; | ||||||
|  |     std::unordered_map<u32, std::unique_ptr<Network::Socket>> open_sockets; | ||||||
|  |     u32 next_socket_id = 3; | ||||||
|  |  | ||||||
|  |     // Cache interface info for the current session | ||||||
|  |     // These two fields are not saved to savestates on purpose | ||||||
|  |     // as network interfaces may change and it's better to. | ||||||
|  |     // obtain them again between play sessions. | ||||||
|  |     bool interface_info_cached = false; | ||||||
|  |     InterfaceInfo interface_info; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| std::shared_ptr<SOC_U> GetService(Core::System& system); | std::shared_ptr<SOC_U> GetService(Core::System& system); | ||||||
|   | |||||||
							
								
								
									
										747
									
								
								src/core/network/network.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										747
									
								
								src/core/network/network.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,747 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <cstring> | ||||||
|  | #include <limits> | ||||||
|  | #include <utility> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "common/error.h" | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  | #include <winsock2.h> | ||||||
|  | #include <ws2tcpip.h> | ||||||
|  | #elif CITRA_UNIX | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <netdb.h> | ||||||
|  | #include <netinet/in.h> | ||||||
|  | #include <poll.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #else | ||||||
|  | #error "Unimplemented platform" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/network/network.h" | ||||||
|  | #include "core/network/network_interface.h" | ||||||
|  | #include "core/network/sockets.h" | ||||||
|  |  | ||||||
|  | namespace Network { | ||||||
|  |  | ||||||
|  | namespace { | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  |  | ||||||
|  | using socklen_t = int; | ||||||
|  |  | ||||||
|  | #define ERRNO(x) WSA##x | ||||||
|  |  | ||||||
|  | void Initialize() { | ||||||
|  |     WSADATA wsa_data; | ||||||
|  |     (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Finalize() { | ||||||
|  |     WSACleanup(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | sockaddr TranslateFromSockAddrIn(CTRSockAddr ctr_addr) { | ||||||
|  |     sockaddr result; | ||||||
|  |     ASSERT_MSG(ctr_addr.raw.len == sizeof(CTRSockAddr), | ||||||
|  |                "Unhandled address size ({})", static_cast<u32>(ctr_addr.raw.len)); | ||||||
|  |     result.sa_family = ctr_addr.raw.sa_family; | ||||||
|  |     std::memset(result.sa_data, 0, sizeof(result.sa_data)); | ||||||
|  |  | ||||||
|  | #if CITRA_UNIX | ||||||
|  |     result.sin_len = sizeof(result); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     // We can not guarantee ABI compatibility between platforms so we copy the fields manually | ||||||
|  |     const auto domain = static_cast<Domain>(result.sa_family); | ||||||
|  |     switch (domain) { | ||||||
|  |     case Domain::INET: { | ||||||
|  |         sockaddr_in result_in; | ||||||
|  |         std::memcpy(&result_in, &result, sizeof(result_in)); | ||||||
|  |         result_in.sin_port = ctr_addr.in.sin_port; | ||||||
|  |         result_in.sin_addr.s_addr = ctr_addr.in.sin_addr; | ||||||
|  |         std::memset(result_in.sin_zero, 0, sizeof(result_in.sin_zero)); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |         ASSERT_MSG(false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform"); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | LINGER MakeLinger(bool enable, u32 linger_value) { | ||||||
|  |     ASSERT(linger_value <= std::numeric_limits<u_short>::max()); | ||||||
|  |  | ||||||
|  |     LINGER value; | ||||||
|  |     value.l_onoff = enable ? 1 : 0; | ||||||
|  |     value.l_linger = static_cast<u_short>(linger_value); | ||||||
|  |     return value; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool EnableNonBlock(SOCKET fd, bool enable) { | ||||||
|  |     u_long value = enable ? 1 : 0; | ||||||
|  |     return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #elif CITRA_UNIX // ^ _WIN32 v CITRA_UNIX | ||||||
|  |  | ||||||
|  | using SOCKET = int; | ||||||
|  | using WSAPOLLFD = pollfd; | ||||||
|  | using ULONG = u64; | ||||||
|  |  | ||||||
|  | constexpr SOCKET SOCKET_ERROR = -1; | ||||||
|  |  | ||||||
|  | constexpr int SD_RECEIVE = SHUT_RD; | ||||||
|  | constexpr int SD_SEND = SHUT_WR; | ||||||
|  | constexpr int SD_BOTH = SHUT_RDWR; | ||||||
|  |  | ||||||
|  | #define ERRNO(x) x | ||||||
|  |  | ||||||
|  | void Initialize() {} | ||||||
|  |  | ||||||
|  | void Finalize() {} | ||||||
|  |  | ||||||
|  | sockaddr TranslateFromSockAddrIn(SockAddrIn input) { | ||||||
|  |     sockaddr_in result; | ||||||
|  |  | ||||||
|  |     switch (static_cast<Domain>(input.family)) { | ||||||
|  |     case Domain::INET: | ||||||
|  |         result.sin_family = AF_INET; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.family); | ||||||
|  |         result.sin_family = AF_INET; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     result.sin_port = htons(input.portno); | ||||||
|  |  | ||||||
|  |     result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24; | ||||||
|  |  | ||||||
|  |     sockaddr addr; | ||||||
|  |     std::memcpy(&addr, &result, sizeof(addr)); | ||||||
|  |     return addr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) { | ||||||
|  |     return poll(fds, static_cast<nfds_t>(nfds), timeout); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int closesocket(SOCKET fd) { | ||||||
|  |     return close(fd); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | linger MakeLinger(bool enable, u32 linger_value) { | ||||||
|  |     linger value; | ||||||
|  |     value.l_onoff = enable ? 1 : 0; | ||||||
|  |     value.l_linger = linger_value; | ||||||
|  |     return value; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool EnableNonBlock(int fd, bool enable) { | ||||||
|  |     int flags = fcntl(fd, F_GETFL); | ||||||
|  |     if (flags == -1) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     if (enable) { | ||||||
|  |         flags |= O_NONBLOCK; | ||||||
|  |     } else { | ||||||
|  |         flags &= ~O_NONBLOCK; | ||||||
|  |     } | ||||||
|  |     return fcntl(fd, F_SETFL, flags) == 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | Errno TranslateNativeError(int e) { | ||||||
|  |     switch (e) { | ||||||
|  |     case E2BIG: | ||||||
|  |         return Errno::TBIG; | ||||||
|  |     case ERRNO(EACCES): | ||||||
|  |         return Errno::ACCES; | ||||||
|  |     case ERRNO(EADDRINUSE): | ||||||
|  |         return Errno::ADDRINUSE; | ||||||
|  |     case ERRNO(EADDRNOTAVAIL): | ||||||
|  |         return Errno::ADDRNOTAVAIL; | ||||||
|  |     case ERRNO(EAFNOSUPPORT): | ||||||
|  |         return Errno::AFNOSUPPORT; | ||||||
|  |     case EAGAIN: | ||||||
|  | #if WIN32 || EAGAIN != EWOULDBLOCK | ||||||
|  |     case ERRNO(EWOULDBLOCK): | ||||||
|  | #endif | ||||||
|  |         return Errno::WOULDBLOCK; | ||||||
|  |     case ERRNO(EALREADY): | ||||||
|  |         return Errno::ALREADY; | ||||||
|  |     case ERRNO(EBADF): | ||||||
|  |         return Errno::BADF; | ||||||
|  |     case EBADMSG: | ||||||
|  |         return Errno::BADMSG; | ||||||
|  |     case EBUSY: | ||||||
|  |         return Errno::BUSY; | ||||||
|  |     case ECANCELED: | ||||||
|  |         return Errno::CANCELED; | ||||||
|  |     case ECHILD: | ||||||
|  |         return Errno::CHILD; | ||||||
|  |     case ERRNO(ECONNABORTED): | ||||||
|  |         return Errno::CONNABORTED; | ||||||
|  |     case ERRNO(ECONNREFUSED): | ||||||
|  |         return Errno::CONNREFUSED; | ||||||
|  |     case ERRNO(ECONNRESET): | ||||||
|  |         return Errno::CONNRESET; | ||||||
|  |     case EDEADLK: | ||||||
|  |         return Errno::DEADLK; | ||||||
|  |     case ERRNO(EDESTADDRREQ): | ||||||
|  |         return Errno::DESTADDRREQ; | ||||||
|  |     case EDOM: | ||||||
|  |         return Errno::DOM; | ||||||
|  |     case ERRNO(EDQUOT): | ||||||
|  |         return Errno::DQUOT; | ||||||
|  |     case EEXIST: | ||||||
|  |         return Errno::EXIST; | ||||||
|  |     case ERRNO(EFAULT): | ||||||
|  |         return Errno::FAULT; | ||||||
|  |     case EFBIG: | ||||||
|  |         return Errno::FBIG; | ||||||
|  |     case ERRNO(EHOSTUNREACH): | ||||||
|  |         return Errno::HOSTUNREACH; | ||||||
|  |     case EIDRM: | ||||||
|  |         return Errno::IDRM; | ||||||
|  |     case EILSEQ: | ||||||
|  |         return Errno::ILSEQ; | ||||||
|  |     case ERRNO(EINPROGRESS): | ||||||
|  |         return Errno::INPROGRESS; | ||||||
|  |     case ERRNO(EINTR): | ||||||
|  |         return Errno::INTR; | ||||||
|  |     case ERRNO(EINVAL): | ||||||
|  |         return Errno::INVAL; | ||||||
|  |     case EIO: | ||||||
|  |         return Errno::IO; | ||||||
|  |     case ERRNO(EISCONN): | ||||||
|  |         return Errno::ISCONN; | ||||||
|  |     case EISDIR: | ||||||
|  |         return Errno::ISDIR; | ||||||
|  |     case ERRNO(ELOOP): | ||||||
|  |         return Errno::LOOP; | ||||||
|  |     case ERRNO(EMFILE): | ||||||
|  |         return Errno::MFILE; | ||||||
|  |     case EMLINK: | ||||||
|  |         return Errno::MLINK; | ||||||
|  |     case ERRNO(EMSGSIZE): | ||||||
|  |         return Errno::MSGSIZE; | ||||||
|  | #ifdef EMULTIHOP | ||||||
|  |     case ERRNO(EMULTIHOP): | ||||||
|  |         return Errno::MULTIHOP; | ||||||
|  | #endif | ||||||
|  |     case ERRNO(ENAMETOOLONG): | ||||||
|  |         return Errno::NAMETOOLONG; | ||||||
|  |     case ERRNO(ENETDOWN): | ||||||
|  |         return Errno::NETDOWN; | ||||||
|  |     case ERRNO(ENETRESET): | ||||||
|  |         return Errno::NETRESET; | ||||||
|  |     case ERRNO(ENETUNREACH): | ||||||
|  |         return Errno::NETUNREACH; | ||||||
|  |     case ENFILE: | ||||||
|  |         return Errno::NFILE; | ||||||
|  |     case ERRNO(ENOBUFS): | ||||||
|  |         return Errno::NOBUFS; | ||||||
|  | #ifdef ENODATA | ||||||
|  |     case ENODATA: | ||||||
|  |         return Errno::NODATA; | ||||||
|  | #endif | ||||||
|  |     case ENODEV: | ||||||
|  |         return Errno::NODEV; | ||||||
|  |     case ENOENT: | ||||||
|  |         return Errno::NOENT; | ||||||
|  |     case ENOEXEC: | ||||||
|  |         return Errno::NOEXEC; | ||||||
|  |     case ENOLCK: | ||||||
|  |         return Errno::NOLCK; | ||||||
|  |     case ENOLINK: | ||||||
|  |         return Errno::NOLINK; | ||||||
|  |     case ENOMEM: | ||||||
|  |         return Errno::NOMEM; | ||||||
|  |     case ENOMSG: | ||||||
|  |         return Errno::NOMSG; | ||||||
|  |     case ERRNO(ENOPROTOOPT): | ||||||
|  |         return Errno::NOPROTOOPT; | ||||||
|  |     case ENOSPC: | ||||||
|  |         return Errno::NOSPC; | ||||||
|  | #ifdef ENOSR | ||||||
|  |     case ENOSR: | ||||||
|  |         return Errno::NOSR; | ||||||
|  | #endif | ||||||
|  | #ifdef ENOSTR | ||||||
|  |     case ENOSTR: | ||||||
|  |         return Errno::NOSTR; | ||||||
|  | #endif | ||||||
|  |     case ENOSYS: | ||||||
|  |         return Errno::NOSYS; | ||||||
|  |     case ERRNO(ENOTCONN): | ||||||
|  |         return Errno::NOTCONN; | ||||||
|  |     case ENOTDIR: | ||||||
|  |         return Errno::NOTDIR; | ||||||
|  |     case ERRNO(ENOTEMPTY): | ||||||
|  |         return Errno::NOTEMPTY; | ||||||
|  |     case ERRNO(ENOTSOCK): | ||||||
|  |         return Errno::NOTSOCK; | ||||||
|  |     case ENOTSUP: | ||||||
|  |         return Errno::NOTSUP; | ||||||
|  |     case ENOTTY: | ||||||
|  |         return Errno::NOTTY; | ||||||
|  |     case ENXIO: | ||||||
|  |         return Errno::NXIO; | ||||||
|  |     case ERRNO(EOPNOTSUPP): | ||||||
|  |         return Errno::OPNOTSUPP; | ||||||
|  |     case EOVERFLOW: | ||||||
|  |         return Errno::OVERFLOW; | ||||||
|  |     case EPERM: | ||||||
|  |         return Errno::PERM; | ||||||
|  |     case EPIPE: | ||||||
|  |         return Errno::PIPE; | ||||||
|  |     case EPROTO: | ||||||
|  |         return Errno::PROTO; | ||||||
|  |     case ERRNO(EPROTONOSUPPORT): | ||||||
|  |         return Errno::PROTONOSUPPORT; | ||||||
|  |     case ERRNO(EPROTOTYPE): | ||||||
|  |         return Errno::PROTOTYPE; | ||||||
|  |     case ERANGE: | ||||||
|  |         return Errno::RANGE; | ||||||
|  |     case EROFS: | ||||||
|  |         return Errno::ROFS; | ||||||
|  |     case ESPIPE: | ||||||
|  |         return Errno::PIPE; | ||||||
|  |     case ESRCH: | ||||||
|  |         return Errno::SRCH; | ||||||
|  |     case ERRNO(ESTALE): | ||||||
|  |         return Errno::STALE; | ||||||
|  | #ifdef ETIME | ||||||
|  |     case ETIME: | ||||||
|  |         return Errno::TIME; | ||||||
|  | #endif | ||||||
|  |     case ERRNO(ETIMEDOUT): | ||||||
|  |         return Errno::TIMEDOUT; | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED_MSG("Unimplemented errno={}", e); | ||||||
|  |         return Errno::OTHER; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno GetAndLogLastError() { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |     int e = WSAGetLastError(); | ||||||
|  | #else | ||||||
|  |     int e = errno; | ||||||
|  | #endif | ||||||
|  |     const Errno err = TranslateNativeError(e); | ||||||
|  |     if (err == Errno::WOULDBLOCK || err == Errno::TIMEDOUT) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |     LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int TranslateDomain(Domain domain) { | ||||||
|  |     switch (domain) { | ||||||
|  |     case Domain::INET: | ||||||
|  |         return AF_INET; | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED_MSG("Unimplemented domain={}", domain); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int TranslateType(Type type) { | ||||||
|  |     switch (type) { | ||||||
|  |     case Type::STREAM: | ||||||
|  |         return SOCK_STREAM; | ||||||
|  |     case Type::DGRAM: | ||||||
|  |         return SOCK_DGRAM; | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED_MSG("Unimplemented type={}", type); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CTRSockAddr TranslateToSockAddrIn(sockaddr input) { | ||||||
|  |     CTRSockAddr result; | ||||||
|  |     result.raw.sa_family = input.sa_family; | ||||||
|  |  | ||||||
|  |     // We can not guarantee ABI compatibility between platforms so we copy the fields manually | ||||||
|  |     const auto domain = static_cast<Domain>(input.sa_family); | ||||||
|  |     switch (domain) { | ||||||
|  |     case Domain::INET: { | ||||||
|  |         sockaddr_in addr_in; | ||||||
|  |         std::memcpy(&addr_in, &input, sizeof(addr_in)); | ||||||
|  |         result.raw.len = sizeof(CTRSockAddr::CTRSockAddrIn); | ||||||
|  |         result.in.sin_port = addr_in.sin_port; | ||||||
|  |         result.in.sin_addr = addr_in.sin_addr.s_addr; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |         ASSERT_MSG(false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform"); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | short TranslatePollEvents(PollEvents events) { | ||||||
|  |     short result = 0; | ||||||
|  |  | ||||||
|  |     if (True(events & PollEvents::In)) { | ||||||
|  |         events &= ~PollEvents::In; | ||||||
|  |         result |= POLLIN; | ||||||
|  |     } | ||||||
|  |     if (True(events & PollEvents::Pri)) { | ||||||
|  |         events &= ~PollEvents::Pri; | ||||||
|  | #ifdef _WIN32 | ||||||
|  |         LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); | ||||||
|  | #else | ||||||
|  |         result |= POLLPRI; | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |     if (True(events & PollEvents::Out)) { | ||||||
|  |         events &= ~PollEvents::Out; | ||||||
|  |         result |= POLLOUT; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events); | ||||||
|  |  | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | PollEvents TranslatePollRevents(short revents) { | ||||||
|  |     PollEvents result{}; | ||||||
|  |     const auto translate = [&result, &revents](short host, PollEvents guest) { | ||||||
|  |         if ((revents & host) != 0) { | ||||||
|  |             revents &= static_cast<short>(~host); | ||||||
|  |             result |= guest; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     translate(POLLIN, PollEvents::In); | ||||||
|  |     translate(POLLPRI, PollEvents::Pri); | ||||||
|  |     translate(POLLOUT, PollEvents::Out); | ||||||
|  |     translate(POLLERR, PollEvents::Err); | ||||||
|  |     translate(POLLHUP, PollEvents::Hup); | ||||||
|  |  | ||||||
|  |     UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); | ||||||
|  |  | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // Anonymous namespace | ||||||
|  |  | ||||||
|  | NetworkInstance::NetworkInstance() { | ||||||
|  |     Initialize(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | NetworkInstance::~NetworkInstance() { | ||||||
|  |     Finalize(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::optional<IPv4Address> GetHostIPv4Address() { | ||||||
|  |     const auto network_interface = Network::GetSelectedNetworkInterface(); | ||||||
|  |     if (!network_interface.has_value()) { | ||||||
|  |         LOG_DEBUG(Network, "GetSelectedNetworkInterface returned no interface"); | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::array<char, 16> ip_addr = {}; | ||||||
|  |     ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) != | ||||||
|  |            nullptr); | ||||||
|  |     return TranslateIPv4(network_interface->ip_address); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { | ||||||
|  |     const size_t num = pollfds.size(); | ||||||
|  |  | ||||||
|  |     std::vector<WSAPOLLFD> host_pollfds(pollfds.size()); | ||||||
|  |     std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { | ||||||
|  |         WSAPOLLFD result; | ||||||
|  |         result.fd = fd.socket->GetFD(); | ||||||
|  |         result.events = TranslatePollEvents(fd.events); | ||||||
|  |         result.revents = 0; | ||||||
|  |         return result; | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout); | ||||||
|  |     if (result == 0) { | ||||||
|  |         ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), | ||||||
|  |                            [](WSAPOLLFD fd) { return fd.revents == 0; })); | ||||||
|  |         return {0, Errno::SUCCESS}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (size_t i = 0; i < num; ++i) { | ||||||
|  |         pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (result > 0) { | ||||||
|  |         return {result, Errno::SUCCESS}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ASSERT(result == SOCKET_ERROR); | ||||||
|  |  | ||||||
|  |     return {-1, GetAndLogLastError()}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Socket::~Socket() { | ||||||
|  |     if (fd == INVALID_SOCKET) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     (void)closesocket(fd); | ||||||
|  |     fd = INVALID_SOCKET; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Socket::Socket(Socket&& rhs) noexcept { | ||||||
|  |     fd = std::exchange(rhs.fd, INVALID_SOCKET); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename T> | ||||||
|  | Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) { | ||||||
|  |     const int result = | ||||||
|  |         setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); | ||||||
|  |     if (result != SOCKET_ERROR) { | ||||||
|  |         return Errno::SUCCESS; | ||||||
|  |     } | ||||||
|  |     return GetAndLogLastError(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::Initialize(Domain domain, Type type, u32 protocol) { | ||||||
|  |     fd = socket(TranslateDomain(domain), TranslateType(type), protocol); | ||||||
|  |     if (fd == INVALID_SOCKET) { | ||||||
|  |         return GetAndLogLastError(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | #if _WIN32 | ||||||
|  |     // Disable UDP connection reset | ||||||
|  |     int new_behavior = 0; | ||||||
|  |     unsigned long bytes_returned = 0; | ||||||
|  |     WSAIoctl(fd, _WSAIOW(IOC_VENDOR, 12), &new_behavior, | ||||||
|  |              sizeof(new_behavior), NULL, 0, &bytes_returned, NULL, NULL); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     return Errno::SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { | ||||||
|  |     sockaddr addr; | ||||||
|  |     socklen_t addrlen = sizeof(addr); | ||||||
|  |     const SOCKET new_socket = accept(fd, &addr, &addrlen); | ||||||
|  |  | ||||||
|  |     if (new_socket == INVALID_SOCKET) { | ||||||
|  |         return {AcceptResult{}, GetAndLogLastError()}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ASSERT(addrlen == sizeof(sockaddr_in)); | ||||||
|  |  | ||||||
|  |     AcceptResult result{ | ||||||
|  |         .socket = std::make_unique<Socket>(new_socket), | ||||||
|  |         .sockaddr_in = TranslateToSockAddrIn(addr), | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     return {std::move(result), Errno::SUCCESS}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::Connect(CTRSockAddr addr_in) { | ||||||
|  |     const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in); | ||||||
|  |     if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != SOCKET_ERROR) { | ||||||
|  |         return Errno::SUCCESS; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return GetAndLogLastError(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::pair<CTRSockAddr, Errno> Socket::GetPeerName() { | ||||||
|  |     sockaddr addr; | ||||||
|  |     socklen_t addrlen = sizeof(addr); | ||||||
|  |     if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { | ||||||
|  |         return {CTRSockAddr{}, GetAndLogLastError()}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ASSERT(addrlen == sizeof(sockaddr_in)); | ||||||
|  |     return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::pair<CTRSockAddr, Errno> Socket::GetSockName() { | ||||||
|  |     sockaddr addr; | ||||||
|  |     socklen_t addrlen = sizeof(addr); | ||||||
|  |     if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { | ||||||
|  |         return {CTRSockAddr{}, GetAndLogLastError()}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     ASSERT(addrlen == sizeof(sockaddr_in)); | ||||||
|  |     return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::Bind(CTRSockAddr addr) { | ||||||
|  |     const sockaddr addr_in = TranslateFromSockAddrIn(addr); | ||||||
|  |     if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) { | ||||||
|  |         return Errno::SUCCESS; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return GetAndLogLastError(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::Listen(s32 backlog) { | ||||||
|  |     if (listen(fd, backlog) != SOCKET_ERROR) { | ||||||
|  |         return Errno::SUCCESS; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return GetAndLogLastError(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::Shutdown(ShutdownHow how) { | ||||||
|  |     int host_how = 0; | ||||||
|  |     switch (how) { | ||||||
|  |     case ShutdownHow::RD: | ||||||
|  |         host_how = SD_RECEIVE; | ||||||
|  |         break; | ||||||
|  |     case ShutdownHow::WR: | ||||||
|  |         host_how = SD_SEND; | ||||||
|  |         break; | ||||||
|  |     case ShutdownHow::RDWR: | ||||||
|  |         host_how = SD_BOTH; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED_MSG("Unimplemented flag how={}", how); | ||||||
|  |         return Errno::SUCCESS; | ||||||
|  |     } | ||||||
|  |     if (shutdown(fd, host_how) != SOCKET_ERROR) { | ||||||
|  |         return Errno::SUCCESS; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return GetAndLogLastError(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { | ||||||
|  |     ASSERT(flags == 0); | ||||||
|  |     ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||||||
|  |  | ||||||
|  |     const auto result = | ||||||
|  |         recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0); | ||||||
|  |     if (result != SOCKET_ERROR) { | ||||||
|  |         return {static_cast<s32>(result), Errno::SUCCESS}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return {-1, GetAndLogLastError()}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, CTRSockAddr* addr) { | ||||||
|  |     ASSERT(flags == 0); | ||||||
|  |     ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||||||
|  |  | ||||||
|  |     sockaddr addr_in{}; | ||||||
|  |     socklen_t addrlen = sizeof(addr_in); | ||||||
|  |     socklen_t* const p_addrlen = addr ? &addrlen : nullptr; | ||||||
|  |     sockaddr* const p_addr_in = addr ? &addr_in : nullptr; | ||||||
|  |  | ||||||
|  |     const auto result = recvfrom(fd, reinterpret_cast<char*>(message.data()), | ||||||
|  |                                  static_cast<int>(message.size()), 0, p_addr_in, p_addrlen); | ||||||
|  |     if (result != SOCKET_ERROR) { | ||||||
|  |         if (addr) { | ||||||
|  |             ASSERT(addrlen == sizeof(addr_in)); | ||||||
|  |             *addr = TranslateToSockAddrIn(addr_in); | ||||||
|  |         } | ||||||
|  |         return {static_cast<s32>(result), Errno::SUCCESS}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return {-1, GetAndLogLastError()}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::pair<s32, Errno> Socket::Send(std::span<const u8> message, int flags) { | ||||||
|  |     ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||||||
|  |     ASSERT(flags == 0); | ||||||
|  |  | ||||||
|  |     const auto result = send(fd, reinterpret_cast<const char*>(message.data()), | ||||||
|  |                              static_cast<int>(message.size()), 0); | ||||||
|  |     if (result != SOCKET_ERROR) { | ||||||
|  |         return {static_cast<s32>(result), Errno::SUCCESS}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return {-1, GetAndLogLastError()}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::pair<s32, Errno> Socket::SendTo(SendFlags flags, std::span<const u8> message, | ||||||
|  |                                      const CTRSockAddr* addr) { | ||||||
|  |     const sockaddr* to = nullptr; | ||||||
|  |     const int to_len = addr ? sizeof(sockaddr) : 0; | ||||||
|  |     sockaddr host_addr_in; | ||||||
|  |  | ||||||
|  |     if (addr) { | ||||||
|  |         host_addr_in = TranslateFromSockAddrIn(*addr); | ||||||
|  |         to = &host_addr_in; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const auto result = sendto(fd, reinterpret_cast<const char*>(message.data()), | ||||||
|  |                                static_cast<int>(message.size()), static_cast<int>(flags), to, to_len); | ||||||
|  |     if (result != SOCKET_ERROR) { | ||||||
|  |         return {static_cast<s32>(result), Errno::SUCCESS}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return {-1, GetAndLogLastError()}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::Close() { | ||||||
|  |     [[maybe_unused]] const int result = closesocket(fd); | ||||||
|  |     ASSERT(result == 0); | ||||||
|  |     fd = INVALID_SOCKET; | ||||||
|  |  | ||||||
|  |     return Errno::SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::SetLinger(bool enable, u32 linger) { | ||||||
|  |     return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::SetReuseAddr(bool enable) { | ||||||
|  |     return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::SetKeepAlive(bool enable) { | ||||||
|  |     return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::SetBroadcast(bool enable) { | ||||||
|  |     return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::SetSndBuf(u32 value) { | ||||||
|  |     return SetSockOpt(fd, SO_SNDBUF, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::SetRcvBuf(u32 value) { | ||||||
|  |     return SetSockOpt(fd, SO_RCVBUF, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::SetSndTimeo(u32 value) { | ||||||
|  |     return SetSockOpt(fd, SO_SNDTIMEO, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::SetRcvTimeo(u32 value) { | ||||||
|  |     return SetSockOpt(fd, SO_RCVTIMEO, value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Errno Socket::SetNonBlock(bool enable) { | ||||||
|  |     blocking = !enable; | ||||||
|  |     if (EnableNonBlock(fd, enable)) { | ||||||
|  |         return Errno::SUCCESS; | ||||||
|  |     } | ||||||
|  |     return GetAndLogLastError(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Socket::IsOpened() const { | ||||||
|  |     return fd != INVALID_SOCKET; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Network | ||||||
							
								
								
									
										150
									
								
								src/core/network/network.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/core/network/network.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  | #include <optional> | ||||||
|  |  | ||||||
|  | #include "common/common_funcs.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/socket_types.h" | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  | #include <winsock2.h> | ||||||
|  | #undef OVERFLOW | ||||||
|  | #elif CITRA_UNIX | ||||||
|  | #include <netinet/in.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace Network { | ||||||
|  |  | ||||||
|  | class SocketBase; | ||||||
|  | class Socket; | ||||||
|  |  | ||||||
|  | /// Error code for network functions | ||||||
|  | enum class Errno { | ||||||
|  |     SUCCESS, | ||||||
|  |     TBIG, | ||||||
|  |     ACCES, | ||||||
|  |     ADDRINUSE, | ||||||
|  |     ADDRNOTAVAIL, | ||||||
|  |     AFNOSUPPORT, | ||||||
|  |     WOULDBLOCK, | ||||||
|  |     ALREADY, | ||||||
|  |     BADF, | ||||||
|  |     BADMSG, | ||||||
|  |     BUSY, | ||||||
|  |     CANCELED, | ||||||
|  |     CHILD, | ||||||
|  |     CONNABORTED, | ||||||
|  |     CONNREFUSED, | ||||||
|  |     CONNRESET, | ||||||
|  |     DEADLK, | ||||||
|  |     DESTADDRREQ, | ||||||
|  |     DOM, | ||||||
|  |     DQUOT, | ||||||
|  |     EXIST, | ||||||
|  |     FAULT, | ||||||
|  |     FBIG, | ||||||
|  |     HOSTUNREACH, | ||||||
|  |     IDRM, | ||||||
|  |     ILSEQ, | ||||||
|  |     INPROGRESS, | ||||||
|  |     INTR, | ||||||
|  |     INVAL, | ||||||
|  |     IO, | ||||||
|  |     ISCONN, | ||||||
|  |     ISDIR, | ||||||
|  |     LOOP, | ||||||
|  |     MFILE, | ||||||
|  |     MLINK, | ||||||
|  |     MSGSIZE, | ||||||
|  |     MULTIHOP, | ||||||
|  |     NAMETOOLONG, | ||||||
|  |     NETDOWN, | ||||||
|  |     NETRESET, | ||||||
|  |     NETUNREACH, | ||||||
|  |     NFILE, | ||||||
|  |     NOBUFS, | ||||||
|  |     NODATA, | ||||||
|  |     NODEV, | ||||||
|  |     NOENT, | ||||||
|  |     NOEXEC, | ||||||
|  |     NOLCK, | ||||||
|  |     NOLINK, | ||||||
|  |     NOMEM, | ||||||
|  |     NOMSG, | ||||||
|  |     NOPROTOOPT, | ||||||
|  |     NOSPC, | ||||||
|  |     NOSR, | ||||||
|  |     NOSTR, | ||||||
|  |     NOSYS, | ||||||
|  |     NOTCONN, | ||||||
|  |     NOTDIR, | ||||||
|  |     NOTEMPTY, | ||||||
|  |     NOTSOCK, | ||||||
|  |     NOTSUP, | ||||||
|  |     NOTTY, | ||||||
|  |     NXIO, | ||||||
|  |     OPNOTSUPP, | ||||||
|  |     OVERFLOW, | ||||||
|  |     PERM, | ||||||
|  |     PIPE, | ||||||
|  |     PROTO, | ||||||
|  |     PROTONOSUPPORT, | ||||||
|  |     PROTOTYPE, | ||||||
|  |     RANGE, | ||||||
|  |     ROFS, | ||||||
|  |     SPIPE, | ||||||
|  |     SRCH, | ||||||
|  |     STALE, | ||||||
|  |     TIME, | ||||||
|  |     TIMEDOUT, | ||||||
|  |     OTHER, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /// Cross-platform poll fd structure | ||||||
|  |  | ||||||
|  | enum class PollEvents : u16 { | ||||||
|  |     // Using Pascal case because IN is a macro on Windows. | ||||||
|  |     In = 1 << 0, | ||||||
|  |     Pri = 1 << 1, | ||||||
|  |     Out = 1 << 2, | ||||||
|  |     Err = 1 << 3, | ||||||
|  |     Hup = 1 << 4, | ||||||
|  |     Nval = 1 << 5, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | DECLARE_ENUM_FLAG_OPERATORS(PollEvents); | ||||||
|  |  | ||||||
|  | struct PollFD { | ||||||
|  |     SocketBase* socket; | ||||||
|  |     PollEvents events; | ||||||
|  |     PollEvents revents; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class NetworkInstance { | ||||||
|  | public: | ||||||
|  |     explicit NetworkInstance(); | ||||||
|  |     ~NetworkInstance(); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  | constexpr IPv4Address TranslateIPv4(in_addr addr) { | ||||||
|  |     auto& bytes = addr.S_un.S_un_b; | ||||||
|  |     return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; | ||||||
|  | } | ||||||
|  | #elif CITRA_UNIX | ||||||
|  | constexpr IPv4Address TranslateIPv4(in_addr addr) { | ||||||
|  |     const u32 bytes = addr.s_addr; | ||||||
|  |     return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), | ||||||
|  |                        static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /// @brief Returns host's IPv4 address | ||||||
|  | /// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array | ||||||
|  | std::optional<IPv4Address> GetHostIPv4Address(); | ||||||
|  |  | ||||||
|  | } // namespace Network | ||||||
							
								
								
									
										218
									
								
								src/core/network/network_interface.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								src/core/network/network_interface.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <fstream> | ||||||
|  | #include <sstream> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "common/bit_cast.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "common/settings.h" | ||||||
|  | #include "common/string_util.h" | ||||||
|  | #include "core/network/network_interface.h" | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  | #include <iphlpapi.h> | ||||||
|  | #else | ||||||
|  | #include <cerrno> | ||||||
|  | #include <ifaddrs.h> | ||||||
|  | #include <net/if.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace Network { | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  |  | ||||||
|  | std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { | ||||||
|  |     std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses; | ||||||
|  |     DWORD ret = ERROR_BUFFER_OVERFLOW; | ||||||
|  |     DWORD buf_size = 0; | ||||||
|  |  | ||||||
|  |     // retry up to 5 times | ||||||
|  |     for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) { | ||||||
|  |         ret = GetAdaptersAddresses( | ||||||
|  |             AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS, | ||||||
|  |             nullptr, adapter_addresses.data(), &buf_size); | ||||||
|  |  | ||||||
|  |         if (ret != ERROR_BUFFER_OVERFLOW) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ret != NO_ERROR) { | ||||||
|  |         LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::vector<NetworkInterface> result; | ||||||
|  |  | ||||||
|  |     for (auto current_address = adapter_addresses.data(); current_address != nullptr; | ||||||
|  |          current_address = current_address->Next) { | ||||||
|  |         if (current_address->FirstUnicastAddress == nullptr || | ||||||
|  |             current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (current_address->OperStatus != IfOperStatusUp) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const auto ip_addr = Common::BitCast<struct sockaddr_in>( | ||||||
|  |                                  *current_address->FirstUnicastAddress->Address.lpSockaddr) | ||||||
|  |                                  .sin_addr; | ||||||
|  |  | ||||||
|  |         ULONG mask = 0; | ||||||
|  |         if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, | ||||||
|  |                                     &mask) != NO_ERROR) { | ||||||
|  |             LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         struct in_addr gateway = {.S_un{.S_addr{0}}}; | ||||||
|  |         if (current_address->FirstGatewayAddress != nullptr && | ||||||
|  |             current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { | ||||||
|  |             gateway = Common::BitCast<struct sockaddr_in>( | ||||||
|  |                           *current_address->FirstGatewayAddress->Address.lpSockaddr) | ||||||
|  |                           .sin_addr; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         result.emplace_back(NetworkInterface{ | ||||||
|  |             .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, | ||||||
|  |             .ip_address{ip_addr}, | ||||||
|  |             .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, | ||||||
|  |             .gateway = gateway}); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #else | ||||||
|  |  | ||||||
|  | std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { | ||||||
|  |     struct ifaddrs* ifaddr = nullptr; | ||||||
|  |  | ||||||
|  |     if (getifaddrs(&ifaddr) != 0) { | ||||||
|  |         LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", | ||||||
|  |                   std::strerror(errno)); | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::vector<NetworkInterface> result; | ||||||
|  |  | ||||||
|  |     for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { | ||||||
|  |         if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (ifa->ifa_addr->sa_family != AF_INET) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         u32 gateway{}; | ||||||
|  |  | ||||||
|  |         std::ifstream file{"/proc/net/route"}; | ||||||
|  |         if (!file.is_open()) { | ||||||
|  |             LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); | ||||||
|  |  | ||||||
|  |             result.emplace_back(NetworkInterface{ | ||||||
|  |                 .name{ifa->ifa_name}, | ||||||
|  |                 .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, | ||||||
|  |                 .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, | ||||||
|  |                 .gateway{in_addr{.s_addr = gateway}}}); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // ignore header | ||||||
|  |         file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); | ||||||
|  |  | ||||||
|  |         bool gateway_found = false; | ||||||
|  |  | ||||||
|  |         for (std::string line; std::getline(file, line);) { | ||||||
|  |             std::istringstream iss{line}; | ||||||
|  |  | ||||||
|  |             std::string iface_name; | ||||||
|  |             iss >> iface_name; | ||||||
|  |             if (iface_name != ifa->ifa_name) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             iss >> std::hex; | ||||||
|  |  | ||||||
|  |             u32 dest{}; | ||||||
|  |             iss >> dest; | ||||||
|  |             if (dest != 0) { | ||||||
|  |                 // not the default route | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             iss >> gateway; | ||||||
|  |  | ||||||
|  |             u16 flags{}; | ||||||
|  |             iss >> flags; | ||||||
|  |  | ||||||
|  |             // flag RTF_GATEWAY (defined in <linux/route.h>) | ||||||
|  |             if ((flags & 0x2) == 0) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             gateway_found = true; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!gateway_found) { | ||||||
|  |             gateway = 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         result.emplace_back(NetworkInterface{ | ||||||
|  |             .name{ifa->ifa_name}, | ||||||
|  |             .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, | ||||||
|  |             .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, | ||||||
|  |             .gateway{in_addr{.s_addr = gateway}}}); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     freeifaddrs(ifaddr); | ||||||
|  |  | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | std::optional<NetworkInterface> GetSelectedNetworkInterface() { | ||||||
|  |     const auto& selected_network_interface = Settings::values.network_interface.GetValue(); | ||||||
|  |     const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); | ||||||
|  |     if (network_interfaces.empty()) { | ||||||
|  |         LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); | ||||||
|  |         return std::nullopt; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const auto res = | ||||||
|  |         std::find_if(network_interfaces.begin(), network_interfaces.end(), [&selected_network_interface](const auto& iface) { | ||||||
|  |             return iface.name == selected_network_interface; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |     if (res == network_interfaces.end()) { | ||||||
|  |         LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); | ||||||
|  |         return std::nullopt; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return *res; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SelectFirstNetworkInterface() { | ||||||
|  |     const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); | ||||||
|  |  | ||||||
|  |     if (network_interfaces.empty()) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Settings::values.network_interface.SetValue(network_interfaces[0].name); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Network | ||||||
							
								
								
									
										31
									
								
								src/core/network/network_interface.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/core/network/network_interface.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <optional> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  | #include <winsock2.h> | ||||||
|  | #else | ||||||
|  | #include <netinet/in.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace Network { | ||||||
|  |  | ||||||
|  | struct NetworkInterface { | ||||||
|  |     std::string name; | ||||||
|  |     struct in_addr ip_address; | ||||||
|  |     struct in_addr subnet_mask; | ||||||
|  |     struct in_addr gateway; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); | ||||||
|  |  | ||||||
|  | std::optional<NetworkInterface> GetSelectedNetworkInterface(); | ||||||
|  |  | ||||||
|  | void SelectFirstNetworkInterface(); | ||||||
|  |  | ||||||
|  | } // namespace Network | ||||||
							
								
								
									
										174
									
								
								src/core/network/sockets.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/core/network/sockets.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | |||||||
|  | // Copyright 2020 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <map> | ||||||
|  | #include <memory> | ||||||
|  | #include <span> | ||||||
|  | #include <utility> | ||||||
|  | #include <boost/serialization/access.hpp> | ||||||
|  |  | ||||||
|  | #if defined(_WIN32) | ||||||
|  | #elif !CITRA_UNIX | ||||||
|  | #error "Platform not implemented" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "core/network/network.h" | ||||||
|  |  | ||||||
|  | namespace Network { | ||||||
|  |  | ||||||
|  | class SocketBase { | ||||||
|  | public: | ||||||
|  | #ifdef CITRA_UNIX | ||||||
|  |     using SOCKET = int; | ||||||
|  |     static constexpr SOCKET INVALID_SOCKET = -1; | ||||||
|  |     static constexpr SOCKET SOCKET_ERROR = -1; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |     struct AcceptResult { | ||||||
|  |         std::unique_ptr<SocketBase> socket; | ||||||
|  |         CTRSockAddr sockaddr_in; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     SocketBase() = default; | ||||||
|  |     explicit SocketBase(SOCKET fd_) : fd{fd_} {} | ||||||
|  |     virtual ~SocketBase() = default; | ||||||
|  |  | ||||||
|  |     CITRA_NON_COPYABLE(SocketBase); | ||||||
|  |     CITRA_NON_MOVEABLE(SocketBase); | ||||||
|  |  | ||||||
|  |     virtual Errno Initialize(Domain domain, Type type, u32 protocol) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno Close() = 0; | ||||||
|  |  | ||||||
|  |     virtual std::pair<AcceptResult, Errno> Accept() = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno Connect(CTRSockAddr addr_in) = 0; | ||||||
|  |  | ||||||
|  |     virtual std::pair<CTRSockAddr, Errno> GetPeerName() = 0; | ||||||
|  |  | ||||||
|  |     virtual std::pair<CTRSockAddr, Errno> GetSockName() = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno Bind(CTRSockAddr addr) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno Listen(s32 backlog) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno Shutdown(ShutdownHow how) = 0; | ||||||
|  |  | ||||||
|  |     virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0; | ||||||
|  |  | ||||||
|  |     virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, | ||||||
|  |                                            CTRSockAddr* addr) = 0; | ||||||
|  |  | ||||||
|  |     virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0; | ||||||
|  |  | ||||||
|  |     virtual std::pair<s32, Errno> SendTo(SendFlags flags, std::span<const u8> message, | ||||||
|  |                                          const CTRSockAddr* addr) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno SetLinger(bool enable, u32 linger) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno SetReuseAddr(bool enable) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno SetKeepAlive(bool enable) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno SetBroadcast(bool enable) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno SetSndBuf(u32 value) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno SetRcvBuf(u32 value) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno SetSndTimeo(u32 value) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno SetRcvTimeo(u32 value) = 0; | ||||||
|  |  | ||||||
|  |     virtual Errno SetNonBlock(bool enable) = 0; | ||||||
|  |  | ||||||
|  |     virtual bool IsOpened() const = 0; | ||||||
|  |  | ||||||
|  |     [[nodiscard]] SOCKET GetFD() const { | ||||||
|  |         return fd; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] bool IsBlocking() const { | ||||||
|  |         return blocking; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     template <class Archive> | ||||||
|  |     void serialize(Archive& ar, const unsigned int) { | ||||||
|  |         ar& fd; | ||||||
|  |         ar& blocking; | ||||||
|  |     } | ||||||
|  |     friend class boost::serialization::access; | ||||||
|  |  | ||||||
|  | protected: | ||||||
|  |     SOCKET fd = INVALID_SOCKET; | ||||||
|  |     bool blocking = true; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class Socket : public SocketBase { | ||||||
|  | public: | ||||||
|  |     Socket() = default; | ||||||
|  |     explicit Socket(SOCKET fd_) : SocketBase{fd_} {} | ||||||
|  |  | ||||||
|  |     ~Socket() override; | ||||||
|  |  | ||||||
|  |     Socket(Socket&& rhs) noexcept; | ||||||
|  |  | ||||||
|  |     Errno Initialize(Domain domain, Type type, u32 protocol) override; | ||||||
|  |  | ||||||
|  |     Errno Close() override; | ||||||
|  |  | ||||||
|  |     std::pair<AcceptResult, Errno> Accept() override; | ||||||
|  |  | ||||||
|  |     Errno Connect(CTRSockAddr addr_in) override; | ||||||
|  |  | ||||||
|  |     std::pair<CTRSockAddr, Errno> GetPeerName() override; | ||||||
|  |  | ||||||
|  |     std::pair<CTRSockAddr, Errno> GetSockName() override; | ||||||
|  |  | ||||||
|  |     Errno Bind(CTRSockAddr addr) override; | ||||||
|  |  | ||||||
|  |     Errno Listen(s32 backlog) override; | ||||||
|  |  | ||||||
|  |     Errno Shutdown(ShutdownHow how) override; | ||||||
|  |  | ||||||
|  |     std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; | ||||||
|  |  | ||||||
|  |     std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, CTRSockAddr* addr) override; | ||||||
|  |  | ||||||
|  |     std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override; | ||||||
|  |  | ||||||
|  |     std::pair<s32, Errno> SendTo(SendFlags flags, std::span<const u8> message, | ||||||
|  |                                  const CTRSockAddr* addr) override; | ||||||
|  |  | ||||||
|  |     Errno SetLinger(bool enable, u32 linger) override; | ||||||
|  |  | ||||||
|  |     Errno SetReuseAddr(bool enable) override; | ||||||
|  |  | ||||||
|  |     Errno SetKeepAlive(bool enable) override; | ||||||
|  |  | ||||||
|  |     Errno SetBroadcast(bool enable) override; | ||||||
|  |  | ||||||
|  |     Errno SetSndBuf(u32 value) override; | ||||||
|  |  | ||||||
|  |     Errno SetRcvBuf(u32 value) override; | ||||||
|  |  | ||||||
|  |     Errno SetSndTimeo(u32 value) override; | ||||||
|  |  | ||||||
|  |     Errno SetRcvTimeo(u32 value) override; | ||||||
|  |  | ||||||
|  |     Errno SetNonBlock(bool enable) override; | ||||||
|  |  | ||||||
|  |     template <typename T> | ||||||
|  |     Errno SetSockOpt(SOCKET fd, int option, T value); | ||||||
|  |  | ||||||
|  |     bool IsOpened() const override; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); | ||||||
|  |  | ||||||
|  | } // namespace Network | ||||||
| @@ -10,6 +10,7 @@ | |||||||
| #include <vector> | #include <vector> | ||||||
| #include <boost/serialization/vector.hpp> | #include <boost/serialization/vector.hpp> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "common/socket_types.h" | ||||||
| #include "network/room.h" | #include "network/room.h" | ||||||
|  |  | ||||||
| namespace Network { | namespace Network { | ||||||
| @@ -44,6 +45,15 @@ private: | |||||||
|     friend class boost::serialization::access; |     friend class boost::serialization::access; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /// Information about the received proxy packets. | ||||||
|  | struct ProxyPacket { | ||||||
|  |     SockAddrIn local_endpoint; | ||||||
|  |     SockAddrIn remote_endpoint; | ||||||
|  |     Protocol protocol; | ||||||
|  |     bool broadcast; | ||||||
|  |     std::vector<u8> data; | ||||||
|  | }; | ||||||
|  |  | ||||||
| /// Represents a chat message. | /// Represents a chat message. | ||||||
| struct ChatEntry { | struct ChatEntry { | ||||||
|     std::string nickname; ///< Nickname of the client who sent this message. |     std::string nickname; ///< Nickname of the client who sent this message. | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| // Copyright 2015 Citra Emulator Project | // Copyright 2015 Citra Emulator Project | ||||||
| // Licensed under GPLv2 or any later version | // Licensed under GPLv2 or any later version | ||||||
| // Refer to the license.txt file included. | // Refer to the license.txt file included. | ||||||
|  | #pragma optimize("", off) | ||||||
| #include "common/arch.h" | #include "common/arch.h" | ||||||
| #if CITRA_ARCH(x86_64) | #if CITRA_ARCH(x86_64) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user