@ -2,40 +2,712 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
# include "common/platform.h"
# if EMU_PLATFORM == PLATFORM_WINDOWS
# include <winsock2.h>
# include <ws2tcpip.h>
# else
# include <sys/socket.h>
# include <netinet/in.h>
# include <netdb.h>
# include <arpa/inet.h>
# include <fcntl.h>
# include <poll.h>
# endif
# include "common/log.h"
# include "common/scope_exit.h"
# include "core/hle/hle.h"
# include "core/hle/service/soc_u.h"
# include <unordered_map>
# if EMU_PLATFORM == PLATFORM_WINDOWS
# define WSAEAGAIN WSAEWOULDBLOCK
# define WSAEMULTIHOP -1 // Invalid dummy value
# define ERRNO(x) WSA##x
# define GET_ERRNO WSAGetLastError()
# define poll(x, y, z) WSAPoll(x, y, z);
# else
# define ERRNO(x) x
# define GET_ERRNO errno
# define closesocket(x) close(x)
# endif
static const s32 SOCKET_ERROR_VALUE = - 1 ;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace SOC_U
namespace SOC_U {
/// 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 } ,
{ ERRNO ( EAGAIN ) , 6 } ,
{ 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 } ,
{ ERRNO ( EMULTIHOP ) , 36 } ,
{ ERRNO ( ENAMETOOLONG ) , 37 } ,
{ ERRNO ( ENETDOWN ) , 38 } ,
{ ERRNO ( ENETRESET ) , 39 } ,
{ ERRNO ( ENETUNREACH ) , 40 } ,
{ ENFILE , 41 } ,
{ ERRNO ( ENOBUFS ) , 42 } ,
{ ENODATA , 43 } ,
{ ENODEV , 44 } ,
{ ENOENT , 45 } ,
{ ENOEXEC , 46 } ,
{ ENOLCK , 47 } ,
{ ENOLINK , 48 } ,
{ ENOMEM , 49 } ,
{ ENOMSG , 50 } ,
{ ERRNO ( ENOPROTOOPT ) , 51 } ,
{ ENOSPC , 52 } ,
{ ENOSR , 53 } ,
{ ENOSTR , 54 } ,
{ 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 } ,
{ ETIME , 75 } ,
{ ERRNO ( ETIMEDOUT ) , 76 }
} } ;
/// Converts a network error from platform-specific to 3ds-specific
static int TranslateError ( int error ) {
auto found = error_map . find ( error ) ;
if ( found ! = error_map . end ( ) )
return - found - > second ;
return error ;
}
/// Holds information about a particular socket
struct SocketHolder {
u32 socket_fd ; ///< The socket descriptor
bool blocking ; ///< Whether the socket is blocking or not, it is only read on Windows.
} ;
/// Structure to represent the 3ds' pollfd structure, which is different than most implementations
struct CTRPollFD {
u32 fd ; ///< Socket handle
union Events {
u32 hex ; ///< The complete value formed by the flags
BitField < 0 , 1 , u32 > pollin ;
BitField < 1 , 1 , u32 > pollpri ;
BitField < 2 , 1 , u32 > pollhup ;
BitField < 3 , 1 , u32 > pollerr ;
BitField < 4 , 1 , u32 > pollout ;
BitField < 5 , 1 , u32 > pollnval ;
Events & operator = ( const Events & other ) {
hex = other . hex ;
return * this ;
}
/// Translates the resulting events of a Poll operation from platform-specific to 3ds specific
static Events TranslateTo3DS ( u32 input_event ) {
Events ev = { } ;
if ( input_event & POLLIN )
ev . pollin = 1 ;
if ( input_event & POLLPRI )
ev . pollpri = 1 ;
if ( input_event & POLLHUP )
ev . pollhup = 1 ;
if ( input_event & POLLERR )
ev . pollerr = 1 ;
if ( input_event & POLLOUT )
ev . pollout = 1 ;
if ( input_event & POLLNVAL )
ev . pollnval = 1 ;
return ev ;
}
/// Translates the resulting events of a Poll operation from 3ds specific to platform specific
static u32 TranslateToPlatform ( Events input_event ) {
u32 ret = 0 ;
if ( input_event . pollin )
ret | = POLLIN ;
if ( input_event . pollpri )
ret | = POLLPRI ;
if ( input_event . pollhup )
ret | = POLLHUP ;
if ( input_event . pollerr )
ret | = POLLERR ;
if ( input_event . pollout )
ret | = POLLOUT ;
if ( input_event . pollnval )
ret | = POLLNVAL ;
return ret ;
}
} ;
Events events ; ///< Events to poll for (input)
Events revents ; ///< Events received (output)
/// Converts a platform-specific pollfd to a 3ds specific structure
static CTRPollFD FromPlatform ( pollfd const & fd ) {
CTRPollFD result ;
result . events . hex = Events : : TranslateTo3DS ( fd . events ) . hex ;
result . revents . hex = Events : : TranslateTo3DS ( fd . revents ) . hex ;
result . fd = static_cast < u32 > ( fd . fd ) ;
return result ;
}
/// Converts a 3ds specific pollfd to a platform-specific structure
static pollfd ToPlatform ( CTRPollFD const & fd ) {
pollfd result ;
result . events = Events : : TranslateToPlatform ( fd . events ) ;
result . revents = Events : : TranslateToPlatform ( fd . revents ) ;
result . fd = fd . fd ;
return result ;
}
} ;
/// 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 ;
/// Convert a 3DS CTRSockAddr to a platform-specific sockaddr
static sockaddr ToPlatform ( CTRSockAddr const & ctr_addr ) {
sockaddr result ;
result . sa_family = 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 :
_dbg_assert_msg_ ( Service_SOC , 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 > ( addr . sa_family ) ;
// We can not guarantee ABI compatibility between platforms so we copy the fields manually
switch ( result . raw . 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 :
_dbg_assert_msg_ ( Service_SOC , false , " Unhandled address family (sa_family) in CTRSockAddr::ToPlatform " ) ;
break ;
}
return result ;
}
} ;
/// Holds info about the currently open sockets
static std : : unordered_map < u32 , SocketHolder > open_sockets ;
/// Close all open sockets
static void CleanupSockets ( ) {
for ( auto sock : open_sockets )
closesocket ( sock . second . socket_fd ) ;
open_sockets . clear ( ) ;
}
static void Socket ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 domain = cmd_buffer [ 1 ] ; // Address family
u32 type = cmd_buffer [ 2 ] ;
u32 protocol = cmd_buffer [ 3 ] ;
// Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use
if ( protocol ! = 0 ) {
cmd_buffer [ 1 ] = UnimplementedFunction ( ErrorModule : : SOC ) . raw ; // TODO(Subv): Correct error code
return ;
}
if ( domain ! = AF_INET ) {
cmd_buffer [ 1 ] = UnimplementedFunction ( ErrorModule : : SOC ) . raw ; // TODO(Subv): Correct error code
return ;
}
if ( type ! = SOCK_DGRAM & & type ! = SOCK_STREAM ) {
cmd_buffer [ 1 ] = UnimplementedFunction ( ErrorModule : : SOC ) . raw ; // TODO(Subv): Correct error code
return ;
}
u32 socket_handle = static_cast < u32 > ( : : socket ( domain , type , protocol ) ) ;
if ( socket_handle ! = SOCKET_ERROR_VALUE )
open_sockets [ socket_handle ] = { socket_handle , true } ;
int result = 0 ;
if ( socket_handle = = SOCKET_ERROR_VALUE )
result = TranslateError ( GET_ERRNO ) ;
cmd_buffer [ 1 ] = result ;
cmd_buffer [ 2 ] = socket_handle ;
}
static void Bind ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
u32 len = cmd_buffer [ 2 ] ;
CTRSockAddr * ctr_sock_addr = reinterpret_cast < CTRSockAddr * > ( Memory : : GetPointer ( cmd_buffer [ 6 ] ) ) ;
if ( ctr_sock_addr = = nullptr ) {
cmd_buffer [ 1 ] = - 1 ; // TODO(Subv): Correct code
return ;
}
sockaddr sock_addr = CTRSockAddr : : ToPlatform ( * ctr_sock_addr ) ;
int res = : : bind ( socket_handle , & sock_addr , std : : max < u32 > ( sizeof ( sock_addr ) , len ) ) ;
int result = 0 ;
if ( res ! = 0 )
result = TranslateError ( GET_ERRNO ) ;
cmd_buffer [ 2 ] = res ;
cmd_buffer [ 1 ] = result ;
}
static void Fcntl ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
u32 ctr_cmd = cmd_buffer [ 2 ] ;
u32 ctr_arg = cmd_buffer [ 3 ] ;
int result = 0 ;
u32 posix_ret = 0 ; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX)
SCOPE_EXIT ( {
cmd_buffer [ 1 ] = result ;
cmd_buffer [ 2 ] = posix_ret ;
} ) ;
if ( ctr_cmd = = 3 ) { // F_GETFL
# if EMU_PLATFORM == PLATFORM_WINDOWS
posix_ret = 0 ;
auto iter = open_sockets . find ( socket_handle ) ;
if ( iter ! = open_sockets . end ( ) & & iter - > second . blocking = = false )
posix_ret | = 4 ; // O_NONBLOCK
# else
int ret = : : fcntl ( socket_handle , F_GETFL , 0 ) ;
if ( ret = = SOCKET_ERROR_VALUE ) {
result = TranslateError ( GET_ERRNO ) ;
posix_ret = - 1 ;
return ;
}
posix_ret = 0 ;
if ( ret & O_NONBLOCK )
posix_ret | = 4 ; // O_NONBLOCK
# endif
} else if ( ctr_cmd = = 4 ) { // F_SETFL
# if EMU_PLATFORM == PLATFORM_WINDOWS
unsigned long tmp = ( ctr_arg & 4 /* O_NONBLOCK */ ) ? 1 : 0 ;
int ret = ioctlsocket ( socket_handle , FIONBIO , & tmp ) ;
if ( ret = = SOCKET_ERROR_VALUE ) {
result = TranslateError ( GET_ERRNO ) ;
posix_ret = - 1 ;
return ;
}
auto iter = open_sockets . find ( socket_handle ) ;
if ( iter ! = open_sockets . end ( ) )
iter - > second . blocking = ( tmp = = 0 ) ;
# else
int flags = : : fcntl ( socket_handle , F_GETFL , 0 ) ;
if ( flags = = SOCKET_ERROR_VALUE ) {
result = TranslateError ( GET_ERRNO ) ;
posix_ret = - 1 ;
return ;
}
flags & = ~ O_NONBLOCK ;
if ( ctr_arg & 4 ) // O_NONBLOCK
flags | = O_NONBLOCK ;
int ret = : : fcntl ( socket_handle , F_SETFL , flags ) ;
if ( ret = = SOCKET_ERROR_VALUE ) {
result = TranslateError ( GET_ERRNO ) ;
posix_ret = - 1 ;
return ;
}
# endif
} else {
LOG_ERROR ( Service_SOC , " Unsupported command (%d) in fcntl call " ) ;
result = TranslateError ( EINVAL ) ; // TODO: Find the correct error
posix_ret = - 1 ;
return ;
}
}
static void Listen ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
u32 backlog = cmd_buffer [ 2 ] ;
int ret = : : listen ( socket_handle , backlog ) ;
int result = 0 ;
if ( ret ! = 0 )
result = TranslateError ( GET_ERRNO ) ;
cmd_buffer [ 2 ] = ret ;
cmd_buffer [ 1 ] = result ;
}
static void Accept ( Service : : Interface * self ) {
// 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
// performing nonblocking operations and spinlock until the data is available
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
socklen_t max_addr_len = static_cast < socklen_t > ( cmd_buffer [ 2 ] ) ;
sockaddr addr ;
socklen_t addr_len = sizeof ( addr ) ;
u32 ret = static_cast < u32 > ( : : accept ( socket_handle , & addr , & addr_len ) ) ;
if ( ret ! = SOCKET_ERROR_VALUE )
open_sockets [ ret ] = { ret , true } ;
int result = 0 ;
if ( ret = = SOCKET_ERROR_VALUE ) {
result = TranslateError ( GET_ERRNO ) ;
} else {
CTRSockAddr ctr_addr = CTRSockAddr : : FromPlatform ( addr ) ;
Memory : : WriteBlock ( cmd_buffer [ 0x104 > > 2 ] , ( const u8 * ) & ctr_addr , max_addr_len ) ;
}
cmd_buffer [ 2 ] = ret ;
cmd_buffer [ 1 ] = result ;
}
static void GetHostId ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
char name [ 128 ] ;
gethostname ( name , sizeof ( name ) ) ;
hostent * host = gethostbyname ( name ) ;
in_addr * addr = reinterpret_cast < in_addr * > ( host - > h_addr ) ;
cmd_buffer [ 2 ] = addr - > s_addr ;
cmd_buffer [ 1 ] = 0 ;
}
static void Close ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
int ret = 0 ;
open_sockets . erase ( socket_handle ) ;
ret = closesocket ( socket_handle ) ;
int result = 0 ;
if ( ret ! = 0 )
result = TranslateError ( GET_ERRNO ) ;
cmd_buffer [ 2 ] = ret ;
cmd_buffer [ 1 ] = result ;
}
static void SendTo ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
u32 len = cmd_buffer [ 2 ] ;
u32 flags = cmd_buffer [ 3 ] ;
u32 addr_len = cmd_buffer [ 4 ] ;
u8 * input_buff = Memory : : GetPointer ( cmd_buffer [ 8 ] ) ;
CTRSockAddr * ctr_dest_addr = reinterpret_cast < CTRSockAddr * > ( Memory : : GetPointer ( cmd_buffer [ 10 ] ) ) ;
if ( ctr_dest_addr = = nullptr ) {
cmd_buffer [ 1 ] = - 1 ; // TODO(Subv): Find the right error code
return ;
}
int ret = - 1 ;
if ( addr_len > 0 ) {
sockaddr dest_addr = CTRSockAddr : : ToPlatform ( * ctr_dest_addr ) ;
ret = : : sendto ( socket_handle , ( const char * ) input_buff , len , flags , & dest_addr , sizeof ( dest_addr ) ) ;
} else {
ret = : : sendto ( socket_handle , ( const char * ) input_buff , len , flags , nullptr , 0 ) ;
}
int result = 0 ;
if ( ret = = SOCKET_ERROR_VALUE )
result = TranslateError ( GET_ERRNO ) ;
cmd_buffer [ 2 ] = ret ;
cmd_buffer [ 1 ] = result ;
}
static void RecvFrom ( Service : : Interface * self ) {
// 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
// performing nonblocking operations and spinlock until the data is available
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
u32 len = cmd_buffer [ 2 ] ;
u32 flags = cmd_buffer [ 3 ] ;
socklen_t addr_len = static_cast < socklen_t > ( cmd_buffer [ 4 ] ) ;
u8 * output_buff = Memory : : GetPointer ( cmd_buffer [ 0x104 > > 2 ] ) ;
sockaddr src_addr ;
socklen_t src_addr_len = sizeof ( src_addr ) ;
int ret = : : recvfrom ( socket_handle , ( char * ) output_buff , len , flags , & src_addr , & src_addr_len ) ;
if ( cmd_buffer [ 0x1A0 > > 2 ] ! = 0 ) {
CTRSockAddr * ctr_src_addr = reinterpret_cast < CTRSockAddr * > ( Memory : : GetPointer ( cmd_buffer [ 0x1A0 > > 2 ] ) ) ;
* ctr_src_addr = CTRSockAddr : : FromPlatform ( src_addr ) ;
}
int result = 0 ;
int total_received = ret ;
if ( ret = = SOCKET_ERROR_VALUE ) {
result = TranslateError ( GET_ERRNO ) ;
total_received = 0 ;
}
cmd_buffer [ 1 ] = result ;
cmd_buffer [ 2 ] = ret ;
cmd_buffer [ 3 ] = total_received ;
}
static void Poll ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 nfds = cmd_buffer [ 1 ] ;
int timeout = cmd_buffer [ 2 ] ;
CTRPollFD * input_fds = reinterpret_cast < CTRPollFD * > ( Memory : : GetPointer ( cmd_buffer [ 6 ] ) ) ;
CTRPollFD * output_fds = reinterpret_cast < CTRPollFD * > ( Memory : : GetPointer ( cmd_buffer [ 0x104 > > 2 ] ) ) ;
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
// so we have to copy the data
pollfd * platform_pollfd = new pollfd [ nfds ] ;
for ( unsigned current_fds = 0 ; current_fds < nfds ; + + current_fds )
platform_pollfd [ current_fds ] = CTRPollFD : : ToPlatform ( input_fds [ current_fds ] ) ;
int ret = : : poll ( platform_pollfd , nfds , timeout ) ;
// Now update the output pollfd structure
for ( unsigned current_fds = 0 ; current_fds < nfds ; + + current_fds )
output_fds [ current_fds ] = CTRPollFD : : FromPlatform ( platform_pollfd [ current_fds ] ) ;
delete [ ] platform_pollfd ;
int result = 0 ;
if ( ret = = SOCKET_ERROR_VALUE )
result = TranslateError ( GET_ERRNO ) ;
cmd_buffer [ 1 ] = result ;
cmd_buffer [ 2 ] = ret ;
}
static void GetSockName ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
socklen_t ctr_len = cmd_buffer [ 2 ] ;
CTRSockAddr * ctr_dest_addr = reinterpret_cast < CTRSockAddr * > ( Memory : : GetPointer ( cmd_buffer [ 0x104 > > 2 ] ) ) ;
sockaddr dest_addr ;
socklen_t dest_addr_len = sizeof ( dest_addr ) ;
int ret = : : getsockname ( socket_handle , & dest_addr , & dest_addr_len ) ;
if ( ctr_dest_addr ! = nullptr ) {
* ctr_dest_addr = CTRSockAddr : : FromPlatform ( dest_addr ) ;
} else {
cmd_buffer [ 1 ] = - 1 ; // TODO(Subv): Verify error
return ;
}
int result = 0 ;
if ( ret ! = 0 )
result = TranslateError ( GET_ERRNO ) ;
cmd_buffer [ 2 ] = ret ;
cmd_buffer [ 1 ] = result ;
}
static void Shutdown ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
int how = cmd_buffer [ 2 ] ;
int ret = : : shutdown ( socket_handle , how ) ;
int result = 0 ;
if ( ret ! = 0 )
result = TranslateError ( GET_ERRNO ) ;
cmd_buffer [ 2 ] = ret ;
cmd_buffer [ 1 ] = result ;
}
static void GetPeerName ( Service : : Interface * self ) {
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
socklen_t len = cmd_buffer [ 2 ] ;
CTRSockAddr * ctr_dest_addr = reinterpret_cast < CTRSockAddr * > ( Memory : : GetPointer ( cmd_buffer [ 0x104 > > 2 ] ) ) ;
sockaddr dest_addr ;
socklen_t dest_addr_len = sizeof ( dest_addr ) ;
int ret = : : getpeername ( socket_handle , & dest_addr , & dest_addr_len ) ;
if ( ctr_dest_addr ! = nullptr ) {
* ctr_dest_addr = CTRSockAddr : : FromPlatform ( dest_addr ) ;
} else {
cmd_buffer [ 1 ] = - 1 ;
return ;
}
int result = 0 ;
if ( ret ! = 0 )
result = TranslateError ( GET_ERRNO ) ;
cmd_buffer [ 2 ] = ret ;
cmd_buffer [ 1 ] = result ;
}
static void Connect ( Service : : Interface * self ) {
// 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
// performing nonblocking operations and spinlock until the data is available
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
u32 socket_handle = cmd_buffer [ 1 ] ;
socklen_t len = cmd_buffer [ 2 ] ;
CTRSockAddr * ctr_input_addr = reinterpret_cast < CTRSockAddr * > ( Memory : : GetPointer ( cmd_buffer [ 6 ] ) ) ;
if ( ctr_input_addr = = nullptr ) {
cmd_buffer [ 1 ] = - 1 ; // TODO(Subv): Verify error
return ;
}
sockaddr input_addr = CTRSockAddr : : ToPlatform ( * ctr_input_addr ) ;
int ret = : : connect ( socket_handle , & input_addr , sizeof ( input_addr ) ) ;
int result = 0 ;
if ( ret ! = 0 )
result = TranslateError ( GET_ERRNO ) ;
cmd_buffer [ 2 ] = ret ;
cmd_buffer [ 1 ] = result ;
}
static void InitializeSockets ( Service : : Interface * self ) {
// TODO(Subv): Implement
# if EMU_PLATFORM == PLATFORM_WINDOWS
WSADATA data ;
WSAStartup ( MAKEWORD ( 2 , 2 ) , & data ) ;
# endif
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
cmd_buffer [ 1 ] = 0 ;
}
static void ShutdownSockets ( Service : : Interface * self ) {
// TODO(Subv): Implement
CleanupSockets ( ) ;
# if EMU_PLATFORM == PLATFORM_WINDOWS
WSACleanup ( ) ;
# endif
u32 * cmd_buffer = Kernel : : GetCommandBuffer ( ) ;
cmd_buffer [ 1 ] = 0 ;
}
const Interface : : FunctionInfo FunctionTable [ ] = {
{ 0x00010044 , nullptr , " InitializeSockets " } ,
{ 0x000200C2 , nullptr , " socket " } ,
{ 0x00030082 , nullptr , " listen " } ,
{ 0x00040082 , nullptr , " accept " } ,
{ 0x00050084 , nullptr , " bind " } ,
{ 0x00060084 , nullptr , " connect " } ,
{ 0x00010044 , InitializeSockets , " InitializeSockets " } ,
{ 0x000200C2 , Socket , " S ocket" } ,
{ 0x00030082 , Listen , " L isten" } ,
{ 0x00040082 , Accept , " A ccept" } ,
{ 0x00050084 , Bind , " B ind" } ,
{ 0x00060084 , Connect , " C onnect" } ,
{ 0x00070104 , nullptr , " recvfrom_other " } ,
{ 0x00080102 , nullptr , " recvfrom " } ,
{ 0x00080102 , RecvFrom , " RecvF rom" } ,
{ 0x00090106 , nullptr , " sendto_other " } ,
{ 0x000A0106 , nullptr , " sendto " } ,
{ 0x000B0042 , nullptr , " close " } ,
{ 0x000C0082 , nullptr , " shutdown " } ,
{ 0x000D0082 , nullptr , " gethostbyname " } ,
{ 0x000E00C2 , nullptr , " gethostbyaddr " } ,
{ 0x000A0106 , SendTo , " SendT o" } ,
{ 0x000B0042 , Close , " C lose" } ,
{ 0x000C0082 , Shutdown , " S hutdown" } ,
{ 0x000D0082 , nullptr , " GetHostByN ame" } ,
{ 0x000E00C2 , nullptr , " GetHostByA ddr" } ,
{ 0x000F0106 , nullptr , " unknown_resolve_ip " } ,
{ 0x00110102 , nullptr , " getsockopt " } ,
{ 0x00120104 , nullptr , " setsockopt " } ,
{ 0x001300C2 , nullptr , " fcntl " } ,
{ 0x00140084 , nullptr , " poll " } ,
{ 0x00150042 , nullptr , " sockatmark " } ,
{ 0x00160000 , nullptr , " gethostid " } ,
{ 0x00170082 , nullptr , " getsockname " } ,
{ 0x00180082 , nullptr , " getpeername " } ,
{ 0x00190000 , nullptr , " ShutdownSockets " } ,
{ 0x00110102 , nullptr , " GetSockO pt" } ,
{ 0x00120104 , nullptr , " SetSockO pt" } ,
{ 0x001300C2 , Fcntl , " F cntl" } ,
{ 0x00140084 , Poll , " P oll" } ,
{ 0x00150042 , nullptr , " SockAtM ark" } ,
{ 0x00160000 , GetHostId , " GetHostI d" } ,
{ 0x00170082 , GetSockName , " GetSockN ame" } ,
{ 0x00180082 , GetPeerName , " GetPeerN ame" } ,
{ 0x00190000 , ShutdownSockets , " ShutdownSockets " } ,
{ 0x001A00C0 , nullptr , " GetNetworkOpt " } ,
{ 0x001B0040 , nullptr , " ICMPSocket " } ,
{ 0x001C0104 , nullptr , " ICMPPing " } ,
@ -52,4 +724,11 @@ Interface::Interface() {
Register ( FunctionTable , ARRAY_SIZE ( FunctionTable ) ) ;
}
Interface : : ~ Interface ( ) {
CleanupSockets ( ) ;
# if EMU_PLATFORM == PLATFORM_WINDOWS
WSACleanup ( ) ;
# endif
}
} // namespace