@ -2,18 +2,138 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
# include <array>
# include <memory>
# include <string>
# include <utility>
# include <vector>
# include <fmt/format.h>
# include "common/microprofile.h"
# include "common/thread.h"
# include "core/hle/ipc_helpers.h"
# include "core/hle/kernel/thread.h"
# include "core/hle/service/sockets/bsd.h"
# include "core/hle/service/sockets/sockets_translate.h"
# include "core/network/network.h"
# include "core/network/sockets.h"
namespace Service : : Sockets {
namespace {
bool IsConnectionBased ( Type type ) {
switch ( type ) {
case Type : : STREAM :
return true ;
case Type : : DGRAM :
return false ;
default :
UNIMPLEMENTED_MSG ( " Unimplemented type={} " , static_cast < int > ( type ) ) ;
return false ;
}
}
} // Anonymous namespace
void BSD : : PollWork : : Execute ( BSD * bsd ) {
std : : tie ( ret , bsd_errno ) = bsd - > PollImpl ( write_buffer , read_buffer , nfds , timeout ) ;
}
void BSD : : PollWork : : Response ( Kernel : : HLERequestContext & ctx ) {
ctx . WriteBuffer ( write_buffer ) ;
IPC : : ResponseBuilder rb { ctx , 4 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < s32 > ( ret ) ;
rb . PushEnum ( bsd_errno ) ;
}
void BSD : : AcceptWork : : Execute ( BSD * bsd ) {
std : : tie ( ret , bsd_errno ) = bsd - > AcceptImpl ( fd , write_buffer ) ;
}
void BSD : : AcceptWork : : Response ( Kernel : : HLERequestContext & ctx ) {
ctx . WriteBuffer ( write_buffer ) ;
IPC : : ResponseBuilder rb { ctx , 5 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < s32 > ( ret ) ;
rb . PushEnum ( bsd_errno ) ;
rb . Push < u32 > ( static_cast < u32 > ( write_buffer . size ( ) ) ) ;
}
void BSD : : ConnectWork : : Execute ( BSD * bsd ) {
bsd_errno = bsd - > ConnectImpl ( fd , addr ) ;
}
void BSD : : ConnectWork : : Response ( Kernel : : HLERequestContext & ctx ) {
IPC : : ResponseBuilder rb { ctx , 4 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < s32 > ( bsd_errno = = Errno : : SUCCESS ? 0 : - 1 ) ;
rb . PushEnum ( bsd_errno ) ;
}
void BSD : : RecvWork : : Execute ( BSD * bsd ) {
std : : tie ( ret , bsd_errno ) = bsd - > RecvImpl ( fd , flags , message ) ;
}
void BSD : : RecvWork : : Response ( Kernel : : HLERequestContext & ctx ) {
ctx . WriteBuffer ( message ) ;
IPC : : ResponseBuilder rb { ctx , 4 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < s32 > ( ret ) ;
rb . PushEnum ( bsd_errno ) ;
}
void BSD : : RecvFromWork : : Execute ( BSD * bsd ) {
std : : tie ( ret , bsd_errno ) = bsd - > RecvFromImpl ( fd , flags , message , addr ) ;
}
void BSD : : RecvFromWork : : Response ( Kernel : : HLERequestContext & ctx ) {
ctx . WriteBuffer ( message , 0 ) ;
if ( ! addr . empty ( ) ) {
ctx . WriteBuffer ( addr , 1 ) ;
}
IPC : : ResponseBuilder rb { ctx , 5 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < s32 > ( ret ) ;
rb . PushEnum ( bsd_errno ) ;
rb . Push < u32 > ( static_cast < u32 > ( addr . size ( ) ) ) ;
}
void BSD : : SendWork : : Execute ( BSD * bsd ) {
std : : tie ( ret , bsd_errno ) = bsd - > SendImpl ( fd , flags , message ) ;
}
void BSD : : SendWork : : Response ( Kernel : : HLERequestContext & ctx ) {
IPC : : ResponseBuilder rb { ctx , 4 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < s32 > ( ret ) ;
rb . PushEnum ( bsd_errno ) ;
}
void BSD : : SendToWork : : Execute ( BSD * bsd ) {
std : : tie ( ret , bsd_errno ) = bsd - > SendToImpl ( fd , flags , message , addr ) ;
}
void BSD : : SendToWork : : Response ( Kernel : : HLERequestContext & ctx ) {
IPC : : ResponseBuilder rb { ctx , 4 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < s32 > ( ret ) ;
rb . PushEnum ( bsd_errno ) ;
}
void BSD : : RegisterClient ( Kernel : : HLERequestContext & ctx ) {
LOG_WARNING ( Service , " (STUBBED) called " ) ;
IPC : : ResponseBuilder rb { ctx , 3 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < u32 > ( 0 ) ; // bsd errno
rb . Push < s 32> ( 0 ) ; // bsd errno
}
void BSD : : StartMonitoring ( Kernel : : HLERequestContext & ctx ) {
@ -26,20 +146,19 @@ void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
void BSD : : Socket ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const u32 domain = rp . Pop < u32 > ( ) ;
const u32 type = rp . Pop < u32 > ( ) ;
const u32 protocol = rp . Pop < u32 > ( ) ;
u32 domain = rp . Pop < u32 > ( ) ;
u32 type = rp . Pop < u32 > ( ) ;
u32 protocol = rp . Pop < u32 > ( ) ;
LOG_WARNING ( Service , " (STUBBED) called domain={} type={} protocol={} " , domain , type , protocol ) ;
LOG_DEBUG ( Service , " called. domain={} type={} protocol={} " , domain , type , protocol ) ;
u32 fd = next_fd + + ;
const auto [ fd , bsd_errno ] = SocketImpl ( static_cast < Domain > ( domain ) , static_cast < Type > ( type ) ,
static_cast < Protocol > ( protocol ) ) ;
IPC : : ResponseBuilder rb { ctx , 4 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < u 32> ( fd ) ;
rb . Push < u32 > ( 0 ) ; // bsd errno
rb . Push < s 32> ( fd ) ;
rb . Push Enum( bsd_errno ) ;
}
void BSD : : Select ( Kernel : : HLERequestContext & ctx ) {
@ -52,67 +171,658 @@ void BSD::Select(Kernel::HLERequestContext& ctx) {
rb . Push < u32 > ( 0 ) ; // bsd errno
}
void BSD : : Poll ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const s32 nfds = rp . Pop < s32 > ( ) ;
const s32 timeout = rp . Pop < s32 > ( ) ;
LOG_DEBUG ( Service , " called. nfds={} timeout={} " , nfds , timeout ) ;
ExecuteWork ( ctx , " BSD:Poll " , timeout ! = 0 ,
PollWork {
. nfds = nfds ,
. timeout = timeout ,
. read_buffer = ctx . ReadBuffer ( ) ,
. write_buffer = std : : vector < u8 > ( ctx . GetWriteBufferSize ( ) ) ,
} ) ;
}
void BSD : : Accept ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
LOG_DEBUG ( Service , " called. fd={} " , fd ) ;
ExecuteWork ( ctx , " BSD:Accept " , IsBlockingSocket ( fd ) ,
AcceptWork {
. fd = fd ,
. write_buffer = std : : vector < u8 > ( ctx . GetWriteBufferSize ( ) ) ,
} ) ;
}
void BSD : : Bind ( Kernel : : HLERequestContext & ctx ) {
LOG_WARNING ( Service , " (STUBBED) called " ) ;
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
IPC : : ResponseBuilder rb { ctx , 4 } ;
LOG_DEBUG( Service , " called. fd={} addrlen={} " , fd , ctx . GetReadBufferSize ( ) ) ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < u32 > ( 0 ) ; // ret
rb . Push < u32 > ( 0 ) ; // bsd errno
BuildErrnoResponse ( ctx , BindImpl ( fd , ctx . ReadBuffer ( ) ) ) ;
}
void BSD : : Connect ( Kernel : : HLERequestContext & ctx ) {
LOG_WARNING ( Service , " (STUBBED) called " ) ;
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
IPC : : ResponseBuilder rb { ctx , 4 } ;
LOG_DEBUG ( Service , " called. fd={} addrlen={} " , fd , ctx . GetReadBufferSize ( ) ) ;
ExecuteWork ( ctx , " BSD:Connect " , IsBlockingSocket ( fd ) ,
ConnectWork {
. fd = fd ,
. addr = ctx . ReadBuffer ( ) ,
} ) ;
}
void BSD : : GetPeerName ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
LOG_DEBUG ( Service , " called. fd={} " , fd ) ;
std : : vector < u8 > write_buffer ( ctx . GetWriteBufferSize ( ) ) ;
const Errno bsd_errno = GetPeerNameImpl ( fd , write_buffer ) ;
ctx . WriteBuffer ( write_buffer ) ;
IPC : : ResponseBuilder rb { ctx , 5 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < u32 > ( 0 ) ; // ret
rb . Push < u32 > ( 0 ) ; // bsd errno
rb . Push < s32 > ( bsd_errno ! = Errno : : SUCCESS ? - 1 : 0 ) ;
rb . PushEnum ( bsd_errno ) ;
rb . Push < u32 > ( static_cast < u32 > ( write_buffer . size ( ) ) ) ;
}
void BSD : : GetSockName ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
LOG_DEBUG ( Service , " called. fd={} " , fd ) ;
std : : vector < u8 > write_buffer ( ctx . GetWriteBufferSize ( ) ) ;
const Errno bsd_errno = GetSockNameImpl ( fd , write_buffer ) ;
ctx . WriteBuffer ( write_buffer ) ;
IPC : : ResponseBuilder rb { ctx , 5 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < s32 > ( bsd_errno ! = Errno : : SUCCESS ? - 1 : 0 ) ;
rb . PushEnum ( bsd_errno ) ;
rb . Push < u32 > ( static_cast < u32 > ( write_buffer . size ( ) ) ) ;
}
void BSD : : Listen ( Kernel : : HLERequestContext & ctx ) {
LOG_WARNING ( Service , " (STUBBED) called " ) ;
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
const s32 backlog = rp . Pop < s32 > ( ) ;
IPC : : ResponseBuilder rb { ctx , 4 } ;
LOG_DEBUG ( Service , " called. fd={} backlog={} " , fd , backlog ) ;
BuildErrnoResponse ( ctx , ListenImpl ( fd , backlog ) ) ;
}
void BSD : : Fcntl ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
const s32 cmd = rp . Pop < s32 > ( ) ;
const s32 arg = rp . Pop < s32 > ( ) ;
LOG_DEBUG ( Service , " called. fd={} cmd={} arg={} " , fd , cmd , arg ) ;
const auto [ ret , bsd_errno ] = FcntlImpl ( fd , static_cast < FcntlCmd > ( cmd ) , arg ) ;
IPC : : ResponseBuilder rb { ctx , 4 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < u32 > ( 0 ) ; // ret
rb . Push < u32 > ( 0 ) ; // bsd errno
rb . Push < s32> ( ret ) ;
rb . Push Enum( bsd_errno ) ;
}
void BSD : : SetSockOpt ( Kernel : : HLERequestContext & ctx ) {
LOG_WARNING ( Service , " (STUBBED) called " ) ;
IPC: : RequestParser rp { ctx } ;
IPC : : ResponseBuilder rb { ctx , 4 } ;
const s32 fd = rp . Pop < s32 > ( ) ;
const u32 level = rp . Pop < u32 > ( ) ;
const OptName optname = static_cast < OptName > ( rp . Pop < u32 > ( ) ) ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < u32 > ( 0 ) ; // ret
rb . Push < u32 > ( 0 ) ; // bsd errno
const std : : vector < u8 > buffer = ctx . ReadBuffer ( ) ;
const u8 * optval = buffer . empty ( ) ? nullptr : buffer . data ( ) ;
size_t optlen = buffer . size ( ) ;
std : : array < u64 , 2 > values ;
if ( ( optname = = OptName : : SNDTIMEO | | optname = = OptName : : RCVTIMEO ) & & buffer . size ( ) = = 8 ) {
std : : memcpy ( values . data ( ) , buffer . data ( ) , sizeof ( values ) ) ;
optlen = sizeof ( values ) ;
optval = reinterpret_cast < const u8 * > ( values . data ( ) ) ;
}
LOG_DEBUG ( Service , " called. fd={} level={} optname=0x{:x} optlen={} " , fd , level ,
static_cast < u32 > ( optname ) , optlen ) ;
BuildErrnoResponse ( ctx , SetSockOptImpl ( fd , level , optname , optlen , optval ) ) ;
}
void BSD : : Shutdown ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
const s32 how = rp . Pop < s32 > ( ) ;
LOG_DEBUG ( Service , " called. fd={} how={} " , fd , how ) ;
BuildErrnoResponse ( ctx , ShutdownImpl ( fd , how ) ) ;
}
void BSD : : Recv ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
const u32 flags = rp . Pop < u32 > ( ) ;
LOG_DEBUG ( Service , " called. fd={} flags=0x{:x} len={} " , fd , flags , ctx . GetWriteBufferSize ( ) ) ;
ExecuteWork ( ctx , " BSD:Recv " , IsBlockingSocket ( fd ) ,
RecvWork {
. fd = fd ,
. flags = flags ,
. message = std : : vector < u8 > ( ctx . GetWriteBufferSize ( ) ) ,
} ) ;
}
void BSD : : RecvFrom ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
const u32 flags = rp . Pop < u32 > ( ) ;
LOG_DEBUG ( Service , " called. fd={} flags=0x{:x} len={} addrlen={} " , fd , flags ,
ctx . GetWriteBufferSize ( 0 ) , ctx . GetWriteBufferSize ( 1 ) ) ;
ExecuteWork ( ctx , " BSD:RecvFrom " , IsBlockingSocket ( fd ) ,
RecvFromWork {
. fd = fd ,
. flags = flags ,
. message = std : : vector < u8 > ( ctx . GetWriteBufferSize ( 0 ) ) ,
. addr = std : : vector < u8 > ( ctx . GetWriteBufferSize ( 1 ) ) ,
} ) ;
}
void BSD : : Send ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
const u32 flags = rp . Pop < u32 > ( ) ;
LOG_DEBUG ( Service , " called. fd={} flags=0x{:x} len={} " , fd , flags , ctx . GetReadBufferSize ( ) ) ;
ExecuteWork ( ctx , " BSD:Send " , IsBlockingSocket ( fd ) ,
SendWork {
. fd = fd ,
. flags = flags ,
. message = ctx . ReadBuffer ( ) ,
} ) ;
}
void BSD : : SendTo ( Kernel : : HLERequestContext & ctx ) {
LOG_WARNING ( Service , " (STUBBED) called " ) ;
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
const u32 flags = rp . Pop < u32 > ( ) ;
LOG_DEBUG ( Service , " called. fd={} flags=0x{} len={} addrlen={} " , fd , flags ,
ctx . GetReadBufferSize ( 0 ) , ctx . GetReadBufferSize ( 1 ) ) ;
ExecuteWork ( ctx , " BSD:SendTo " , IsBlockingSocket ( fd ) ,
SendToWork {
. fd = fd ,
. flags = flags ,
. message = ctx . ReadBuffer ( 0 ) ,
. addr = ctx . ReadBuffer ( 1 ) ,
} ) ;
}
IPC : : ResponseBuilder rb { ctx , 4 } ;
void BSD : : Write ( Kernel : : HLERequestContext & ctx ) {
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < u32 > ( 0 ) ; // ret
rb . Push < u32 > ( 0 ) ; // bsd errno
LOG_DEBUG ( Service , " called. fd={} len={} " , fd , ctx . GetReadBufferSize ( ) ) ;
ExecuteWork ( ctx , " BSD:Write " , IsBlockingSocket ( fd ) ,
SendWork {
. fd = fd ,
. flags = 0 ,
. message = ctx . ReadBuffer ( ) ,
} ) ;
}
void BSD : : Close ( Kernel : : HLERequestContext & ctx ) {
LOG_WARNING ( Service , " (STUBBED) called " ) ;
IPC : : RequestParser rp { ctx } ;
const s32 fd = rp . Pop < s32 > ( ) ;
LOG_DEBUG ( Service , " called. fd={} " , fd ) ;
BuildErrnoResponse ( ctx , CloseImpl ( fd ) ) ;
}
template < typename Work >
void BSD : : ExecuteWork ( Kernel : : HLERequestContext & ctx , std : : string_view sleep_reason ,
bool is_blocking , Work work ) {
if ( ! is_blocking ) {
work . Execute ( this ) ;
work . Response ( ctx ) ;
return ;
}
// Signal a dummy response to make IPC validation happy
// This will be overwritten by the SleepClientThread callback
work . Response ( ctx ) ;
auto worker = worker_pool . CaptureWorker ( ) ;
ctx . SleepClientThread ( std : : string ( sleep_reason ) , std : : numeric_limits < u64 > : : max ( ) ,
worker - > Callback < Work > ( ) , worker - > KernelEvent ( ) ) ;
worker - > SendWork ( std : : move ( work ) ) ;
}
std : : pair < s32 , Errno > BSD : : SocketImpl ( Domain domain , Type type , Protocol protocol ) {
if ( type = = Type : : SEQPACKET ) {
UNIMPLEMENTED_MSG ( " SOCK_SEQPACKET errno management " ) ;
} else if ( type = = Type : : RAW & & ( domain ! = Domain : : INET | | protocol ! = Protocol : : ICMP ) ) {
UNIMPLEMENTED_MSG ( " SOCK_RAW errno management " ) ;
}
[[maybe_unused]] const bool unk_flag = ( static_cast < u32 > ( type ) & 0x20000000 ) ! = 0 ;
UNIMPLEMENTED_IF_MSG ( unk_flag , " Unknown flag in type " ) ;
type = static_cast < Type > ( static_cast < u32 > ( type ) & ~ 0x20000000 ) ;
const s32 fd = FindFreeFileDescriptorHandle ( ) ;
if ( fd < 0 ) {
LOG_ERROR ( Service , " No more file descriptors available " ) ;
return { - 1 , Errno : : MFILE } ;
}
FileDescriptor & descriptor = file_descriptors [ fd ] . emplace ( ) ;
// ENONMEM might be thrown here
LOG_INFO ( Service , " New socket fd={} " , fd ) ;
descriptor . socket = std : : make_unique < Network : : Socket > ( ) ;
descriptor . socket - > Initialize ( Translate ( domain ) , Translate ( type ) , Translate ( type , protocol ) ) ;
descriptor . is_connection_based = IsConnectionBased ( type ) ;
return { fd , Errno : : SUCCESS } ;
}
std : : pair < s32 , Errno > BSD : : PollImpl ( std : : vector < u8 > & write_buffer , std : : vector < u8 > read_buffer ,
s32 nfds , s32 timeout ) {
if ( write_buffer . size ( ) < nfds * sizeof ( PollFD ) ) {
return { - 1 , Errno : : INVAL } ;
}
const size_t length = std : : min ( read_buffer . size ( ) , write_buffer . size ( ) ) ;
std : : vector < PollFD > fds ( nfds ) ;
std : : memcpy ( fds . data ( ) , read_buffer . data ( ) , length ) ;
if ( timeout > = 0 ) {
const s64 seconds = timeout / 1000 ;
const u64 nanoseconds = 1'000'000 * ( static_cast < u64 > ( timeout ) % 1000 ) ;
if ( seconds < 0 ) {
return { - 1 , Errno : : INVAL } ;
}
if ( nanoseconds > 999'999'999 ) {
return { - 1 , Errno : : INVAL } ;
}
} else if ( timeout ! = - 1 ) {
return { - 1 , Errno : : INVAL } ;
}
for ( PollFD & pollfd : fds ) {
ASSERT ( pollfd . revents = = 0 ) ;
if ( pollfd . fd > MAX_FD | | pollfd . fd < 0 ) {
LOG_ERROR ( Service , " File descriptor handle={} is invalid " , pollfd . fd ) ;
pollfd . revents = 0 ;
return { 0 , Errno : : SUCCESS } ;
}
std : : optional < FileDescriptor > & descriptor = file_descriptors [ pollfd . fd ] ;
if ( ! descriptor ) {
LOG_ERROR ( Service , " File descriptor handle={} is not allocated " , pollfd . fd ) ;
pollfd . revents = POLL_NVAL ;
return { 0 , Errno : : SUCCESS } ;
}
}
std : : vector < Network : : PollFD > host_pollfds ( fds . size ( ) ) ;
std : : transform ( fds . begin ( ) , fds . end ( ) , host_pollfds . begin ( ) , [ this ] ( PollFD pollfd ) {
Network : : PollFD result ;
result . socket = file_descriptors [ pollfd . fd ] - > socket . get ( ) ;
result . events = TranslatePollEventsToHost ( pollfd . events ) ;
result . revents = 0 ;
return result ;
} ) ;
const auto result = Network : : Poll ( host_pollfds , timeout ) ;
const size_t num = host_pollfds . size ( ) ;
for ( size_t i = 0 ; i < num ; + + i ) {
fds [ i ] . revents = TranslatePollEventsToGuest ( host_pollfds [ i ] . revents ) ;
}
std : : memcpy ( write_buffer . data ( ) , fds . data ( ) , length ) ;
return Translate ( result ) ;
}
std : : pair < s32 , Errno > BSD : : AcceptImpl ( s32 fd , std : : vector < u8 > & write_buffer ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return { - 1 , Errno : : BADF } ;
}
const s32 new_fd = FindFreeFileDescriptorHandle ( ) ;
if ( new_fd < 0 ) {
LOG_ERROR ( Service , " No more file descriptors available " ) ;
return { - 1 , Errno : : MFILE } ;
}
FileDescriptor & descriptor = * file_descriptors [ fd ] ;
auto [ result , bsd_errno ] = descriptor . socket - > Accept ( ) ;
if ( bsd_errno ! = Network : : Errno : : SUCCESS ) {
return { - 1 , Translate ( bsd_errno ) } ;
}
FileDescriptor & new_descriptor = file_descriptors [ new_fd ] . emplace ( ) ;
new_descriptor . socket = std : : move ( result . socket ) ;
new_descriptor . is_connection_based = descriptor . is_connection_based ;
ASSERT ( write_buffer . size ( ) = = sizeof ( SockAddrIn ) ) ;
const SockAddrIn guest_addr_in = Translate ( result . sockaddr_in ) ;
std : : memcpy ( write_buffer . data ( ) , & guest_addr_in , sizeof ( guest_addr_in ) ) ;
return { new_fd , Errno : : SUCCESS } ;
}
Errno BSD : : BindImpl ( s32 fd , const std : : vector < u8 > & addr ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return Errno : : BADF ;
}
ASSERT ( addr . size ( ) = = sizeof ( SockAddrIn ) ) ;
SockAddrIn addr_in ;
std : : memcpy ( & addr_in , addr . data ( ) , sizeof ( addr_in ) ) ;
return Translate ( file_descriptors [ fd ] - > socket - > Bind ( Translate ( addr_in ) ) ) ;
}
Errno BSD : : ConnectImpl ( s32 fd , const std : : vector < u8 > & addr ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return Errno : : BADF ;
}
UNIMPLEMENTED_IF ( addr . size ( ) ! = sizeof ( SockAddrIn ) ) ;
SockAddrIn addr_in ;
std : : memcpy ( & addr_in , addr . data ( ) , sizeof ( addr_in ) ) ;
return Translate ( file_descriptors [ fd ] - > socket - > Connect ( Translate ( addr_in ) ) ) ;
}
Errno BSD : : GetPeerNameImpl ( s32 fd , std : : vector < u8 > & write_buffer ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return Errno : : BADF ;
}
const auto [ addr_in , bsd_errno ] = file_descriptors [ fd ] - > socket - > GetPeerName ( ) ;
if ( bsd_errno ! = Network : : Errno : : SUCCESS ) {
return Translate ( bsd_errno ) ;
}
const SockAddrIn guest_addrin = Translate ( addr_in ) ;
ASSERT ( write_buffer . size ( ) = = sizeof ( guest_addrin ) ) ;
std : : memcpy ( write_buffer . data ( ) , & guest_addrin , sizeof ( guest_addrin ) ) ;
return Translate ( bsd_errno ) ;
}
Errno BSD : : GetSockNameImpl ( s32 fd , std : : vector < u8 > & write_buffer ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return Errno : : BADF ;
}
const auto [ addr_in , bsd_errno ] = file_descriptors [ fd ] - > socket - > GetSockName ( ) ;
if ( bsd_errno ! = Network : : Errno : : SUCCESS ) {
return Translate ( bsd_errno ) ;
}
const SockAddrIn guest_addrin = Translate ( addr_in ) ;
ASSERT ( write_buffer . size ( ) = = sizeof ( guest_addrin ) ) ;
std : : memcpy ( write_buffer . data ( ) , & guest_addrin , sizeof ( guest_addrin ) ) ;
return Translate ( bsd_errno ) ;
}
Errno BSD : : ListenImpl ( s32 fd , s32 backlog ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return Errno : : BADF ;
}
return Translate ( file_descriptors [ fd ] - > socket - > Listen ( backlog ) ) ;
}
std : : pair < s32 , Errno > BSD : : FcntlImpl ( s32 fd , FcntlCmd cmd , s32 arg ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return { - 1 , Errno : : BADF } ;
}
FileDescriptor & descriptor = * file_descriptors [ fd ] ;
switch ( cmd ) {
case FcntlCmd : : GETFL :
ASSERT ( arg = = 0 ) ;
return { descriptor . flags , Errno : : SUCCESS } ;
case FcntlCmd : : SETFL : {
const bool enable = ( arg & FLAG_O_NONBLOCK ) ! = 0 ;
const Errno bsd_errno = Translate ( descriptor . socket - > SetNonBlock ( enable ) ) ;
if ( bsd_errno ! = Errno : : SUCCESS ) {
return { - 1 , bsd_errno } ;
}
descriptor . flags = arg ;
return { 0 , Errno : : SUCCESS } ;
}
default :
UNIMPLEMENTED_MSG ( " Unimplemented cmd={} " , static_cast < int > ( cmd ) ) ;
return { - 1 , Errno : : SUCCESS } ;
}
}
Errno BSD : : SetSockOptImpl ( s32 fd , u32 level , OptName optname , size_t optlen , const void * optval ) {
UNIMPLEMENTED_IF ( level ! = 0xffff ) ; // SOL_SOCKET
if ( ! IsFileDescriptorValid ( fd ) ) {
return Errno : : BADF ;
}
Network : : Socket * const socket = file_descriptors [ fd ] - > socket . get ( ) ;
if ( optname = = OptName : : LINGER ) {
ASSERT ( optlen = = sizeof ( Linger ) ) ;
Linger linger ;
std : : memcpy ( & linger , optval , sizeof ( linger ) ) ;
ASSERT ( linger . onoff = = 0 | | linger . onoff = = 1 ) ;
return Translate ( socket - > SetLinger ( linger . onoff ! = 0 , linger . linger ) ) ;
}
ASSERT ( optlen = = sizeof ( u32 ) ) ;
u32 value ;
std : : memcpy ( & value , optval , sizeof ( value ) ) ;
switch ( optname ) {
case OptName : : REUSEADDR :
ASSERT ( value = = 0 | | value = = 1 ) ;
return Translate ( socket - > SetReuseAddr ( value ! = 0 ) ) ;
case OptName : : BROADCAST :
ASSERT ( value = = 0 | | value = = 1 ) ;
return Translate ( socket - > SetBroadcast ( value ! = 0 ) ) ;
case OptName : : SNDBUF :
return Translate ( socket - > SetSndBuf ( value ) ) ;
case OptName : : RCVBUF :
return Translate ( socket - > SetRcvBuf ( value ) ) ;
case OptName : : SNDTIMEO :
return Translate ( socket - > SetSndTimeo ( value ) ) ;
case OptName : : RCVTIMEO :
return Translate ( socket - > SetRcvTimeo ( value ) ) ;
default :
UNIMPLEMENTED_MSG ( " Unimplemented optname={} " , static_cast < int > ( optname ) ) ;
return Errno : : SUCCESS ;
}
}
Errno BSD : : ShutdownImpl ( s32 fd , s32 how ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return Errno : : BADF ;
}
const Network : : ShutdownHow host_how = Translate ( static_cast < ShutdownHow > ( how ) ) ;
return Translate ( file_descriptors [ fd ] - > socket - > Shutdown ( host_how ) ) ;
}
std : : pair < s32 , Errno > BSD : : RecvImpl ( s32 fd , u32 flags , std : : vector < u8 > & message ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return { - 1 , Errno : : BADF } ;
}
return Translate ( file_descriptors [ fd ] - > socket - > Recv ( flags , message ) ) ;
}
std : : pair < s32 , Errno > BSD : : RecvFromImpl ( s32 fd , u32 flags , std : : vector < u8 > & message ,
std : : vector < u8 > & addr ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return { - 1 , Errno : : BADF } ;
}
FileDescriptor & descriptor = * file_descriptors [ fd ] ;
Network : : SockAddrIn addr_in { } ;
Network : : SockAddrIn * p_addr_in = nullptr ;
if ( descriptor . is_connection_based ) {
// Connection based file descriptors (e.g. TCP) zero addr
addr . clear ( ) ;
} else {
p_addr_in = & addr_in ;
}
// Apply flags
if ( ( flags & FLAG_MSG_DONTWAIT ) ! = 0 ) {
flags & = ~ FLAG_MSG_DONTWAIT ;
if ( ( descriptor . flags & FLAG_O_NONBLOCK ) = = 0 ) {
descriptor . socket - > SetNonBlock ( true ) ;
}
}
const auto [ ret , bsd_errno ] = Translate ( descriptor . socket - > RecvFrom ( flags , message , p_addr_in ) ) ;
// Restore original state
if ( ( descriptor . flags & FLAG_O_NONBLOCK ) = = 0 ) {
descriptor . socket - > SetNonBlock ( false ) ;
}
if ( p_addr_in ) {
if ( ret < 0 ) {
addr . clear ( ) ;
} else {
ASSERT ( addr . size ( ) = = sizeof ( SockAddrIn ) ) ;
const SockAddrIn result = Translate ( addr_in ) ;
std : : memcpy ( addr . data ( ) , & result , sizeof ( result ) ) ;
}
}
return { ret , bsd_errno } ;
}
std : : pair < s32 , Errno > BSD : : SendImpl ( s32 fd , u32 flags , const std : : vector < u8 > & message ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return { - 1 , Errno : : BADF } ;
}
return Translate ( file_descriptors [ fd ] - > socket - > Send ( message , flags ) ) ;
}
std : : pair < s32 , Errno > BSD : : SendToImpl ( s32 fd , u32 flags , const std : : vector < u8 > & message ,
const std : : vector < u8 > & addr ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return { - 1 , Errno : : BADF } ;
}
Network : : SockAddrIn addr_in ;
Network : : SockAddrIn * p_addr_in = nullptr ;
if ( ! addr . empty ( ) ) {
ASSERT ( addr . size ( ) = = sizeof ( SockAddrIn ) ) ;
SockAddrIn guest_addr_in ;
std : : memcpy ( & guest_addr_in , addr . data ( ) , sizeof ( guest_addr_in ) ) ;
addr_in = Translate ( guest_addr_in ) ;
}
return Translate ( file_descriptors [ fd ] - > socket - > SendTo ( flags , message , p_addr_in ) ) ;
}
Errno BSD : : CloseImpl ( s32 fd ) {
if ( ! IsFileDescriptorValid ( fd ) ) {
return Errno : : BADF ;
}
const Errno bsd_errno = Translate ( file_descriptors [ fd ] - > socket - > Close ( ) ) ;
if ( bsd_errno ! = Errno : : SUCCESS ) {
return bsd_errno ;
}
LOG_INFO ( Service , " Close socket fd={} " , fd ) ;
file_descriptors [ fd ] . reset ( ) ;
return bsd_errno ;
}
s32 BSD : : FindFreeFileDescriptorHandle ( ) noexcept {
for ( s32 fd = 0 ; fd < static_cast < s32 > ( file_descriptors . size ( ) ) ; + + fd ) {
if ( ! file_descriptors [ fd ] ) {
return fd ;
}
}
return - 1 ;
}
bool BSD : : IsFileDescriptorValid ( s32 fd ) const noexcept {
if ( fd > MAX_FD | | fd < 0 ) {
LOG_ERROR ( Service , " Invalid file descriptor handle={} " , fd ) ;
return false ;
}
if ( ! file_descriptors [ fd ] ) {
LOG_ERROR ( Service , " File descriptor handle={} is not allocated " , fd ) ;
return false ;
}
return true ;
}
bool BSD : : IsBlockingSocket ( s32 fd ) const noexcept {
// Inform invalid sockets as non-blocking
// This way we avoid using a worker thread as it will fail without blocking host
if ( fd > MAX_FD | | fd < 0 ) {
return false ;
}
if ( ! file_descriptors [ fd ] ) {
return false ;
}
return ( file_descriptors [ fd ] - > flags & FLAG_O_NONBLOCK ) ! = 0 ;
}
void BSD : : BuildErrnoResponse ( Kernel : : HLERequestContext & ctx , Errno bsd_errno ) const noexcept {
IPC : : ResponseBuilder rb { ctx , 4 } ;
rb . Push ( RESULT_SUCCESS ) ;
rb . Push < u32 > ( 0 ) ; // ret
rb . Push < u32 > ( 0 ) ; // bsd errno
rb . Push < s32> ( bsd_errno = = Errno : : SUCCESS ? 0 : - 1 ) ;
rb . Push Enum( bsd_errno ) ;
}
BSD : : BSD ( const char * name ) : ServiceFramework ( name ) {
BSD : : BSD ( Core : : System & system , const char * name )
: ServiceFramework ( name ) , worker_pool { system , this } {
// clang-format off
static const FunctionInfo functions [ ] = {
{ 0 , & BSD : : RegisterClient , " RegisterClient " } ,
@ -121,25 +831,25 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
{ 3 , nullptr , " SocketExempt " } ,
{ 4 , nullptr , " Open " } ,
{ 5 , & BSD : : Select , " Select " } ,
{ 6 , nullptr , " Poll " } ,
{ 6 , & BSD : : Poll , " Poll " } ,
{ 7 , nullptr , " Sysctl " } ,
{ 8 , nullptr , " Recv " } ,
{ 9 , nullptr , " RecvFrom " } ,
{ 10 , nullptr , " Send " } ,
{ 8 , & BSD : : Recv , " Recv " } ,
{ 9 , & BSD : : RecvFrom , " RecvFrom " } ,
{ 10 , & BSD : : Send , " Send " } ,
{ 11 , & BSD : : SendTo , " SendTo " } ,
{ 12 , nullptr , " Accept " } ,
{ 12 , & BSD : : Accept , " Accept " } ,
{ 13 , & BSD : : Bind , " Bind " } ,
{ 14 , & BSD : : Connect , " Connect " } ,
{ 15 , nullptr , " GetPeerName " } ,
{ 16 , nullptr , " GetSockName " } ,
{ 15 , & BSD : : GetPeerName , " GetPeerName " } ,
{ 16 , & BSD : : GetSockName , " GetSockName " } ,
{ 17 , nullptr , " GetSockOpt " } ,
{ 18 , & BSD : : Listen , " Listen " } ,
{ 19 , nullptr , " Ioctl " } ,
{ 20 , nullptr , " Fcntl " } ,
{ 20 , & BSD : : Fcntl , " Fcntl " } ,
{ 21 , & BSD : : SetSockOpt , " SetSockOpt " } ,
{ 22 , nullptr , " Shutdown " } ,
{ 22 , & BSD : : Shutdown , " Shutdown " } ,
{ 23 , nullptr , " ShutdownAllSockets " } ,
{ 24 , nullptr , " Write " } ,
{ 24 , & BSD : : Write , " Write " } ,
{ 25 , nullptr , " Read " } ,
{ 26 , & BSD : : Close , " Close " } ,
{ 27 , nullptr , " DuplicateSocket " } ,