2020-01-18 11:38:21 +03:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2019-08-10 18:58:06 +03:00
# include <AK/StringBuilder.h>
2019-06-07 10:36:51 +03:00
# include <Kernel/FileSystem/FileDescription.h>
2019-04-03 13:25:24 +03:00
# include <Kernel/FileSystem/VirtualFileSystem.h>
2019-06-07 12:43:58 +03:00
# include <Kernel/Net/LocalSocket.h>
# include <Kernel/Process.h>
2020-01-11 14:07:45 +03:00
# include <Kernel/StdLib.h>
2019-06-07 12:43:58 +03:00
# include <Kernel/UnixTypes.h>
2019-02-14 16:38:30 +03:00
# include <LibC/errno_numbers.h>
2019-02-14 16:17:38 +03:00
2019-03-01 12:51:58 +03:00
//#define DEBUG_LOCAL_SOCKET
2019-08-10 18:58:06 +03:00
Lockable < InlineLinkedList < LocalSocket > > & LocalSocket : : all_sockets ( )
{
static Lockable < InlineLinkedList < LocalSocket > > * s_list ;
if ( ! s_list )
s_list = new Lockable < InlineLinkedList < LocalSocket > > ( ) ;
return * s_list ;
}
void LocalSocket : : for_each ( Function < void ( LocalSocket & ) > callback )
{
LOCKER ( all_sockets ( ) . lock ( ) ) ;
for ( auto & socket : all_sockets ( ) . resource ( ) )
callback ( socket ) ;
}
2020-01-23 20:11:14 +03:00
KResultOr < NonnullRefPtr < Socket > > LocalSocket : : create ( int type )
2019-02-14 16:17:38 +03:00
{
return adopt ( * new LocalSocket ( type ) ) ;
}
LocalSocket : : LocalSocket ( int type )
: Socket ( AF_LOCAL , type , 0 )
{
2019-08-10 18:58:06 +03:00
LOCKER ( all_sockets ( ) . lock ( ) ) ;
all_sockets ( ) . resource ( ) . append ( this ) ;
2019-12-06 20:38:36 +03:00
2020-01-03 22:14:56 +03:00
m_prebind_uid = current - > process ( ) . uid ( ) ;
m_prebind_gid = current - > process ( ) . gid ( ) ;
m_prebind_mode = 0666 ;
2019-03-01 12:51:58 +03:00
# ifdef DEBUG_LOCAL_SOCKET
2019-03-24 00:03:17 +03:00
kprintf ( " %s(%u) LocalSocket{%p} created with type=%u \n " , current - > process ( ) . name ( ) . characters ( ) , current - > pid ( ) , this , type ) ;
2019-03-01 12:51:58 +03:00
# endif
2019-02-14 16:17:38 +03:00
}
LocalSocket : : ~ LocalSocket ( )
{
2019-08-10 18:58:06 +03:00
LOCKER ( all_sockets ( ) . lock ( ) ) ;
all_sockets ( ) . resource ( ) . remove ( this ) ;
2019-02-14 16:17:38 +03:00
}
2020-02-08 01:42:28 +03:00
void LocalSocket : : get_local_address ( sockaddr * address , socklen_t * address_size )
2019-02-14 17:17:30 +03:00
{
2020-02-08 01:42:28 +03:00
size_t bytes_to_copy = min ( static_cast < size_t > ( * address_size ) , sizeof ( sockaddr_un ) ) ;
memcpy ( address , & m_address , bytes_to_copy ) ;
2019-02-14 17:17:30 +03:00
* address_size = sizeof ( sockaddr_un ) ;
}
2020-02-08 01:42:28 +03:00
void LocalSocket : : get_peer_address ( sockaddr * address , socklen_t * address_size )
2019-05-20 21:33:03 +03:00
{
2020-02-08 01:42:28 +03:00
get_local_address ( address , address_size ) ;
2019-05-20 21:33:03 +03:00
}
2020-01-11 14:07:45 +03:00
KResult LocalSocket : : bind ( const sockaddr * user_address , socklen_t address_size )
2019-02-14 16:38:30 +03:00
{
2019-08-10 06:17:00 +03:00
ASSERT ( setup_state ( ) = = SetupState : : Unstarted ) ;
2019-03-07 00:14:31 +03:00
if ( address_size ! = sizeof ( sockaddr_un ) )
return KResult ( - EINVAL ) ;
2020-01-11 14:07:45 +03:00
sockaddr_un address ;
copy_from_user ( & address , user_address , sizeof ( sockaddr_un ) ) ;
if ( address . sun_family ! = AF_LOCAL )
2019-03-07 00:14:31 +03:00
return KResult ( - EINVAL ) ;
2019-02-14 16:38:30 +03:00
2020-01-11 14:07:45 +03:00
auto path = String ( address . sun_path , strnlen ( address . sun_path , sizeof ( address . sun_path ) ) ) ;
2019-02-14 16:38:30 +03:00
2019-03-01 12:51:58 +03:00
# ifdef DEBUG_LOCAL_SOCKET
2019-03-24 00:03:17 +03:00
kprintf ( " %s(%u) LocalSocket{%p} bind(%s) \n " , current - > process ( ) . name ( ) . characters ( ) , current - > pid ( ) , this , safe_address ) ;
2019-03-01 12:51:58 +03:00
# endif
2019-02-14 16:38:30 +03:00
2020-01-09 23:32:11 +03:00
mode_t mode = S_IFSOCK | ( m_prebind_mode & 04777 ) ;
2020-01-03 22:14:56 +03:00
UidAndGid owner { m_prebind_uid , m_prebind_gid } ;
2020-01-21 15:14:26 +03:00
auto result = VFS : : the ( ) . open ( path , O_CREAT | O_EXCL | O_NOFOLLOW_NOERROR , mode , current - > process ( ) . current_directory ( ) , owner ) ;
2019-03-07 00:14:31 +03:00
if ( result . is_error ( ) ) {
if ( result . error ( ) = = - EEXIST )
return KResult ( - EADDRINUSE ) ;
return result . error ( ) ;
2019-02-14 16:38:30 +03:00
}
2019-02-14 17:17:30 +03:00
2020-01-31 00:15:45 +03:00
auto file = move ( result . value ( ) ) ;
ASSERT ( file - > inode ( ) ) ;
if ( ! file - > inode ( ) - > bind_socket ( * this ) )
return KResult ( - EADDRINUSE ) ;
m_file = move ( file ) ;
2019-02-14 17:55:19 +03:00
2020-01-11 14:07:45 +03:00
m_address = address ;
2019-02-14 17:17:30 +03:00
m_bound = true ;
2019-03-07 00:14:31 +03:00
return KSuccess ;
2019-02-14 16:38:30 +03:00
}
2019-02-14 17:55:19 +03:00
2019-06-13 23:03:04 +03:00
KResult LocalSocket : : connect ( FileDescription & description , const sockaddr * address , socklen_t address_size , ShouldBlock )
2019-02-14 17:55:19 +03:00
{
ASSERT ( ! m_bound ) ;
2019-03-07 00:14:31 +03:00
if ( address_size ! = sizeof ( sockaddr_un ) )
return KResult ( - EINVAL ) ;
if ( address - > sa_family ! = AF_LOCAL )
return KResult ( - EINVAL ) ;
2020-01-09 23:30:56 +03:00
if ( is_connected ( ) )
return KResult ( - EISCONN ) ;
2019-02-14 17:55:19 +03:00
const sockaddr_un & local_address = * reinterpret_cast < const sockaddr_un * > ( address ) ;
2019-08-10 18:49:14 +03:00
char safe_address [ sizeof ( local_address . sun_path ) + 1 ] = { 0 } ;
2019-02-14 17:55:19 +03:00
memcpy ( safe_address , local_address . sun_path , sizeof ( local_address . sun_path ) ) ;
2019-03-01 12:51:58 +03:00
# ifdef DEBUG_LOCAL_SOCKET
2019-03-24 00:03:17 +03:00
kprintf ( " %s(%u) LocalSocket{%p} connect(%s) \n " , current - > process ( ) . name ( ) . characters ( ) , current - > pid ( ) , this , safe_address ) ;
2019-03-01 12:51:58 +03:00
# endif
2019-02-14 17:55:19 +03:00
2020-01-19 01:04:48 +03:00
auto description_or_error = VFS : : the ( ) . open ( safe_address , O_RDWR , 0 , current - > process ( ) . current_directory ( ) ) ;
2019-06-13 23:03:04 +03:00
if ( description_or_error . is_error ( ) )
2019-03-07 00:14:31 +03:00
return KResult ( - ECONNREFUSED ) ;
2019-08-10 18:58:06 +03:00
2019-06-13 23:03:04 +03:00
m_file = move ( description_or_error . value ( ) ) ;
2019-03-07 00:14:31 +03:00
2019-02-14 17:55:19 +03:00
ASSERT ( m_file - > inode ( ) ) ;
2019-03-07 00:14:31 +03:00
if ( ! m_file - > inode ( ) - > socket ( ) )
return KResult ( - ECONNREFUSED ) ;
2019-02-14 17:55:19 +03:00
m_address = local_address ;
2019-02-14 19:18:35 +03:00
2019-08-11 16:38:20 +03:00
ASSERT ( m_connect_side_fd = = & description ) ;
m_connect_side_role = Role : : Connecting ;
2019-02-14 19:18:35 +03:00
auto peer = m_file - > inode ( ) - > socket ( ) ;
2019-03-07 00:14:31 +03:00
auto result = peer - > queue_connection_from ( * this ) ;
2019-08-11 16:38:20 +03:00
if ( result . is_error ( ) ) {
m_connect_side_role = Role : : None ;
2019-03-07 00:14:31 +03:00
return result ;
2019-08-11 16:38:20 +03:00
}
2019-02-14 19:18:35 +03:00
2019-08-11 16:38:20 +03:00
if ( is_connected ( ) ) {
m_connect_side_role = Role : : Connected ;
2019-07-20 12:10:46 +03:00
return KSuccess ;
2019-08-11 16:38:20 +03:00
}
2019-07-20 12:10:46 +03:00
2020-01-10 21:15:01 +03:00
if ( current - > block < Thread : : ConnectBlocker > ( description ) ! = Thread : : BlockResult : : WokeNormally ) {
2019-08-11 16:38:20 +03:00
m_connect_side_role = Role : : None ;
2019-07-20 12:10:46 +03:00
return KResult ( - EINTR ) ;
2019-08-11 16:38:20 +03:00
}
2019-07-20 12:10:46 +03:00
2019-08-10 06:17:00 +03:00
# ifdef DEBUG_LOCAL_SOCKET
kprintf ( " %s(%u) LocalSocket{%p} connect(%s) status is %s \n " , current - > process ( ) . name ( ) . characters ( ) , current - > pid ( ) , this , safe_address , to_string ( setup_state ( ) ) ) ;
# endif
2019-08-11 16:38:20 +03:00
if ( ! is_connected ( ) ) {
m_connect_side_role = Role : : None ;
2019-07-20 12:10:46 +03:00
return KResult ( - ECONNREFUSED ) ;
2019-08-11 16:38:20 +03:00
}
m_connect_side_role = Role : : Connected ;
2019-07-20 12:10:46 +03:00
return KSuccess ;
2019-02-14 17:55:19 +03:00
}
2019-02-14 18:01:08 +03:00
2019-08-06 16:40:38 +03:00
KResult LocalSocket : : listen ( int backlog )
{
LOCKER ( lock ( ) ) ;
if ( type ( ) ! = SOCK_STREAM )
return KResult ( - EOPNOTSUPP ) ;
set_backlog ( backlog ) ;
2019-08-11 16:38:20 +03:00
m_connect_side_role = m_role = Role : : Listener ;
2019-10-18 17:47:29 +03:00
# ifdef DEBUG_LOCAL_SOCKET
2019-08-06 16:40:38 +03:00
kprintf ( " LocalSocket{%p} listening with backlog=%d \n " , this , backlog ) ;
2019-10-18 17:47:29 +03:00
# endif
2019-08-06 16:40:38 +03:00
return KSuccess ;
}
2019-06-13 23:03:04 +03:00
void LocalSocket : : attach ( FileDescription & description )
2019-02-17 02:13:47 +03:00
{
2019-08-11 16:28:18 +03:00
ASSERT ( ! m_accept_side_fd_open ) ;
2019-08-11 16:38:20 +03:00
if ( m_connect_side_role = = Role : : None ) {
ASSERT ( m_connect_side_fd = = nullptr ) ;
m_connect_side_fd = & description ;
} else {
ASSERT ( m_connect_side_fd ! = & description ) ;
2019-08-11 16:28:18 +03:00
m_accept_side_fd_open = true ;
2019-02-17 13:00:35 +03:00
}
}
2019-06-13 23:03:04 +03:00
void LocalSocket : : detach ( FileDescription & description )
2019-02-17 13:00:35 +03:00
{
2019-08-11 16:38:20 +03:00
if ( m_connect_side_fd = = & description ) {
m_connect_side_fd = nullptr ;
} else {
2019-08-11 16:28:18 +03:00
ASSERT ( m_accept_side_fd_open ) ;
m_accept_side_fd_open = false ;
2019-02-17 13:00:35 +03:00
}
2019-02-17 02:13:47 +03:00
}
2019-11-04 16:03:14 +03:00
bool LocalSocket : : can_read ( const FileDescription & description ) const
2019-02-14 18:01:08 +03:00
{
2019-08-11 16:38:20 +03:00
auto role = this - > role ( description ) ;
if ( role = = Role : : Listener )
2019-02-14 18:03:37 +03:00
return can_accept ( ) ;
2019-08-11 16:38:20 +03:00
if ( role = = Role : : Accepted )
2019-06-13 23:03:04 +03:00
return ! has_attached_peer ( description ) | | ! m_for_server . is_empty ( ) ;
2019-08-11 16:38:20 +03:00
if ( role = = Role : : Connected )
2019-06-13 23:03:04 +03:00
return ! has_attached_peer ( description ) | | ! m_for_client . is_empty ( ) ;
2019-11-10 00:15:35 +03:00
return false ;
2019-02-14 18:01:08 +03:00
}
2019-06-13 23:03:04 +03:00
bool LocalSocket : : has_attached_peer ( const FileDescription & description ) const
2019-05-20 03:54:52 +03:00
{
2019-08-11 16:38:20 +03:00
auto role = this - > role ( description ) ;
if ( role = = Role : : Accepted )
return m_connect_side_fd ! = nullptr ;
if ( role = = Role : : Connected )
2019-08-11 16:28:18 +03:00
return m_accept_side_fd_open ;
2019-05-20 03:54:52 +03:00
ASSERT_NOT_REACHED ( ) ;
}
2019-11-04 16:03:14 +03:00
bool LocalSocket : : can_write ( const FileDescription & description ) const
2019-02-14 18:01:08 +03:00
{
2019-08-11 16:38:20 +03:00
auto role = this - > role ( description ) ;
if ( role = = Role : : Accepted )
2020-01-20 18:08:49 +03:00
return ! has_attached_peer ( description ) | | m_for_client . space_for_writing ( ) ;
2019-08-11 16:38:20 +03:00
if ( role = = Role : : Connected )
2020-01-20 18:08:49 +03:00
return ! has_attached_peer ( description ) | | m_for_server . space_for_writing ( ) ;
2019-11-10 00:15:35 +03:00
return false ;
2019-02-14 18:01:08 +03:00
}
2019-03-12 17:51:42 +03:00
2019-06-13 23:03:04 +03:00
ssize_t LocalSocket : : sendto ( FileDescription & description , const void * data , size_t data_size , int , const sockaddr * , socklen_t )
2019-03-12 17:51:42 +03:00
{
2019-08-05 11:03:19 +03:00
if ( ! has_attached_peer ( description ) )
return - EPIPE ;
2019-12-01 19:36:06 +03:00
ssize_t nwritten = send_buffer_for ( description ) . write ( ( const u8 * ) data , data_size ) ;
if ( nwritten > 0 )
current - > did_unix_socket_write ( nwritten ) ;
return nwritten ;
}
DoubleBuffer & LocalSocket : : receive_buffer_for ( FileDescription & description )
{
2019-08-11 16:38:20 +03:00
auto role = this - > role ( description ) ;
if ( role = = Role : : Accepted )
2019-12-01 19:36:06 +03:00
return m_for_server ;
2019-08-11 16:38:20 +03:00
if ( role = = Role : : Connected )
2019-12-01 19:36:06 +03:00
return m_for_client ;
2019-08-05 11:03:19 +03:00
ASSERT_NOT_REACHED ( ) ;
2019-03-12 17:51:42 +03:00
}
2019-03-12 19:27:07 +03:00
2019-12-01 19:36:06 +03:00
DoubleBuffer & LocalSocket : : send_buffer_for ( FileDescription & description )
2019-03-12 19:27:07 +03:00
{
2019-08-11 16:38:20 +03:00
auto role = this - > role ( description ) ;
2019-09-22 22:30:30 +03:00
if ( role = = Role : : Connected )
2019-12-01 19:36:06 +03:00
return m_for_server ;
if ( role = = Role : : Accepted )
2019-09-22 22:30:30 +03:00
return m_for_client ;
ASSERT_NOT_REACHED ( ) ;
}
ssize_t LocalSocket : : recvfrom ( FileDescription & description , void * buffer , size_t buffer_size , int , sockaddr * , socklen_t * )
{
2019-12-01 19:36:06 +03:00
auto & buffer_for_me = receive_buffer_for ( description ) ;
2019-09-22 22:30:30 +03:00
if ( ! description . is_blocking ( ) ) {
if ( buffer_for_me . is_empty ( ) ) {
if ( ! has_attached_peer ( description ) )
return 0 ;
return - EAGAIN ;
2019-08-05 11:03:19 +03:00
}
2019-09-22 22:30:30 +03:00
} else if ( ! can_read ( description ) ) {
2020-01-26 19:54:23 +03:00
auto result = current - > block < Thread : : ReadBlocker > ( description ) ;
2020-01-10 21:15:01 +03:00
if ( result ! = Thread : : BlockResult : : WokeNormally )
2019-09-22 22:30:30 +03:00
return - EINTR ;
2019-08-05 11:03:19 +03:00
}
2019-09-22 22:30:30 +03:00
if ( ! has_attached_peer ( description ) & & buffer_for_me . is_empty ( ) )
return 0 ;
ASSERT ( ! buffer_for_me . is_empty ( ) ) ;
2019-12-01 19:36:06 +03:00
int nread = buffer_for_me . read ( ( u8 * ) buffer , buffer_size ) ;
if ( nread > 0 )
current - > did_unix_socket_read ( nread ) ;
return nread ;
2019-03-12 19:27:07 +03:00
}
2019-08-10 18:55:54 +03:00
StringView LocalSocket : : socket_path ( ) const
{
2019-12-09 19:45:40 +03:00
size_t len = strnlen ( m_address . sun_path , sizeof ( m_address . sun_path ) ) ;
2019-08-10 18:55:54 +03:00
return { m_address . sun_path , len } ;
}
2019-08-10 19:10:36 +03:00
String LocalSocket : : absolute_path ( const FileDescription & description ) const
{
StringBuilder builder ;
builder . append ( " socket: " ) ;
builder . append ( socket_path ( ) ) ;
switch ( role ( description ) ) {
case Role : : Listener :
builder . append ( " (listening) " ) ;
break ;
case Role : : Accepted :
builder . appendf ( " (accepted from pid %d) " , origin_pid ( ) ) ;
break ;
case Role : : Connected :
builder . appendf ( " (connected to pid %d) " , acceptor_pid ( ) ) ;
break ;
case Role : : Connecting :
builder . append ( " (connecting) " ) ;
break ;
default :
break ;
}
return builder . to_string ( ) ;
}
2019-12-06 20:38:36 +03:00
KResult LocalSocket : : getsockopt ( FileDescription & description , int level , int option , void * value , socklen_t * value_size )
{
if ( level ! = SOL_SOCKET )
return Socket : : getsockopt ( description , level , option , value , value_size ) ;
switch ( option ) {
case SO_PEERCRED : {
if ( * value_size < sizeof ( ucred ) )
return KResult ( - EINVAL ) ;
auto & creds = * ( ucred * ) value ;
switch ( role ( description ) ) {
case Role : : Accepted :
creds = m_origin ;
* value_size = sizeof ( ucred ) ;
return KSuccess ;
case Role : : Connected :
creds = m_acceptor ;
* value_size = sizeof ( ucred ) ;
return KSuccess ;
case Role : : Connecting :
return KResult ( - ENOTCONN ) ;
default :
return KResult ( - EINVAL ) ;
}
break ;
}
default :
return Socket : : getsockopt ( description , level , option , value , value_size ) ;
}
}
2020-01-03 22:14:56 +03:00
KResult LocalSocket : : chmod ( mode_t mode )
{
if ( m_file )
return m_file - > chmod ( mode ) ;
m_prebind_mode = mode & 04777 ;
return KSuccess ;
}
KResult LocalSocket : : chown ( uid_t uid , gid_t gid )
{
if ( m_file )
return m_file - > chown ( uid , gid ) ;
if ( ! current - > process ( ) . is_superuser ( ) & & ( current - > process ( ) . euid ( ) ! = uid | | ! current - > process ( ) . in_group ( gid ) ) )
return KResult ( - EPERM ) ;
m_prebind_uid = uid ;
m_prebind_gid = gid ;
return KSuccess ;
}