📄 tcp.c
字号:
/* Since BeOS doesn't support checking for writeability or errors, we
have to clear these values before we call select() so that the
caller won't find anything still set when we return */
if( write_bits != NULL )
FD_ZERO( write_bits );
if( exception_bits != NULL )
FD_ZERO( exception_bits );
return( select( socket_range, read_bits, NULL, NULL, timeout ) );
}
#define select( sockets, readFD, writeFD, exceptFD, timeout ) \
my_select( sockets, readFD, writeFD, exceptFD, timeout )
static int my_setsockopt( int socket, int level, int option,
const void *data, uint size )
{
if( option != SO_NONBLOCK && option != SO_REUSEADDR )
return( 0 );
return( setsockopt( socket, level, option, data, size ) );
}
static int my_getsockopt( int socket, int level, int option,
void *data, uint *size )
{
BYTE buffer[ 8 + 8 ];
int count;
if( option != SO_ERROR )
return( 0 );
*( ( int * ) data ) = 0; /* Clear return status */
/* It's unclear whether the following setsockopt actually does anything
under BeOS or not. If it fails, the alternative below may work */
#if 1
return( setsockopt( socket, level, option, data, *size ) );
#else
count = recv( socket, buffer, 0, 0 );
printf( "recv( 0 ) = %d, errno = %d.\n", count, errno );
if( count < 0 )
*( ( int * ) data ) = errno;
#endif /* 1 */
}
#endif /* BeOS without BONE */
/****************************************************************************
* *
* Network Socket Manager *
* *
****************************************************************************/
/* cryptlib's separation kernel causes some problems with objects that use
sockets because it doesn't allow sharing of sockets, which is a problem
because the Unix server programming model assumes that a single process
will listen on a socket and fork off children to handle incoming
connections (in fact the accept() function more or less forces you to do
this whether you want to or not). A second problem occurs because when a
thread is blocked in an object waiting on a socket there's no way to
unblock it apart from killing the thread (actually we could create some
sort of lookback socket and wait for it alongside the listen socket in
the pre-accept select wait, signalling a shutdown by closing the loopback
socket, but this starts to get ugly). In order to work around this we
maintain a socket pool that serves two functions:
- Maintains a list of sockets that an object is listening on to allow a
listening socket to be reused rather than having to listen on a
socket and close it as soon as an incoming connection is made in
order to switch to the connected socket.
- Allows sockets to be closed from another thread, which results in any
objects waiting on them being woken up and exiting.
For now we limit the socket pool to a maximum of 256 sockets (16 in
resource-constrained environments) both as a safety feature to protect
against runaway use of sockets in the calling application and because
cryptlib was never designed to function as a high-volume server
application. If necessary this can be changed to dynamically expand the
socket pool in the same way that the kernel dynamically expands its
object table. However it's not a good idea to simply remove the
restriction entirely (or set it to too high a value) because this can
cause problems with excess consumption of kernel resources. For example
under Windows opening several tens of thousands of connections will
eventually return WSAENOBUFS when the nonpaged pool is exhausted. At
this point things start to get problematic because many drivers don't
handle the inability to allocate memory very well, and can start to fail
and render the whole system unstable. This is a general resource-
consumption problem that affects all users of the shared nonpaged pool,
but we can at least make sure that we're not the cause of any crashes by
limiting our own consumption */
#ifdef CONFIG_CONSERVE_MEMORY
#define SOCKETPOOL_SIZE 16
#else
#define SOCKETPOOL_SIZE 256
#endif /* CONFIG_CONSERVE_MEMORY */
typedef struct {
SOCKET netSocket; /* Socket handle */
int refCount; /* Reference count for the socket */
int iChecksum; /* Family, interface, and port */
BYTE iData[ 32 + 8 ]; /* info for server socket */
int iDataLen;
} SOCKET_INFO;
static SOCKET_INFO *socketInfo;
static const SOCKET_INFO SOCKET_INFO_TEMPLATE = \
{ INVALID_SOCKET, 0, 0, { 0 }, 0 };
/* Initialise and shut down the socket pool */
CHECK_RETVAL \
static int initSocketPool( void )
{
int i;
/* Allocate and clear the socket pool */
if( ( socketInfo = \
clAlloc( "initSocketPool", SOCKETPOOL_SIZE * \
sizeof( SOCKET_INFO ) ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
for( i = 0; i < SOCKETPOOL_SIZE; i++ )
socketInfo[ i ] = SOCKET_INFO_TEMPLATE;
return( CRYPT_OK );
}
static void endSocketPool( void )
{
clFree( "endSocketPool", socketInfo );
}
/* Create/add and remove a socket to/from the pool. The difference between
creating and adding a socket is that newSocket() creates and adds a
completely new socket while addSocket() adds an externally-created (via
accept()) socket */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int newSocket( OUT SOCKET *newSocketPtr,
const struct addrinfo *addrInfoPtr,
const BOOLEAN isServer )
{
SOCKET netSocket;
int iCheck = DUMMY_INIT, i, status;
assert( isWritePtr( newSocketPtr, sizeof( SOCKET ) ) );
assert( isReadPtr( addrInfoPtr, sizeof( struct addrinfo ) ) );
/* Clear return value */
*newSocketPtr = INVALID_SOCKET;
/* Perform any required pre-calculations before we acquire the mutex */
if( isServer )
{
iCheck = checksumData( addrInfoPtr->ai_addr,
addrInfoPtr->ai_addrlen );
}
status = krnlEnterMutex( MUTEX_SOCKETPOOL );
if( cryptStatusError( status ) )
return( status );
/* If this is a server socket (i.e. one bound to a specific interface and
port), check to see whether there's already a socket bound here and if
there is, return the existing socket rather than creating a new one.
This check isn't currently totally foolproof since it compares some
nonessential fields that may differ for otherwise identical sockets
(it's difficult to do this in a clean manner because the comparison
becomes very protocol- and implementation-specific). A workaround
would be to check whether the sin_family is AF_INET or AF_INET6 and
perform an appropriate situation-specific comparison, but this will
break the nice portability that was added by the RFC 2553
reorganisation of socket functions for IPv6 */
if( isServer )
{
for( i = 0; i < SOCKETPOOL_SIZE; i++ )
{
if( socketInfo[ i ].refCount > 0 && \
socketInfo[ i ].iChecksum == iCheck && \
socketInfo[ i ].iDataLen == addrInfoPtr->ai_addrlen && \
!memcmp( socketInfo[ i ].iData, addrInfoPtr->ai_addr,
addrInfoPtr->ai_addrlen ) )
{
if( socketInfo[ i ].refCount >= 10000 )
{
krnlExitMutex( MUTEX_SOCKETPOOL );
assert( DEBUG_WARN );
return( CRYPT_ERROR_OVERFLOW );
}
ENSURES( socketInfo[ i ].refCount > 0 && \
socketInfo[ i ].refCount < 10000 );
ENSURES( !isBadSocket( socketInfo[ i ].netSocket ) );
socketInfo[ i ].refCount++;
*newSocketPtr = socketInfo[ i ].netSocket;
krnlExitMutex( MUTEX_SOCKETPOOL );
/* The socket already exists, don't perform any further
initialisation with it */
return( CRYPT_OK );
}
}
}
/* Create a new socket entry */
for( i = 0; i < SOCKETPOOL_SIZE; i++ )
{
/* Check whether this is a zombie socket that we couldn't close
earlier, usually due to written data being left in the TCP/IP
stack. As a result it's probably trapped in the TIME_WAIT
state, so we periodically try and close it to free up the
resource */
if( socketInfo[ i ].refCount <= 0 && \
!isBadSocket( socketInfo[ i ].netSocket ) )
{
status = closesocket( socketInfo[ i ].netSocket );
if( !isSocketError( status ) )
socketInfo[ i ] = SOCKET_INFO_TEMPLATE;
}
if( isBadSocket( socketInfo[ i ].netSocket ) )
break;
}
if( i >= SOCKETPOOL_SIZE )
{
krnlExitMutex( MUTEX_SOCKETPOOL );
assert( DEBUG_WARN );
return( CRYPT_ERROR_OVERFLOW ); /* Should never happen */
}
netSocket = socket( addrInfoPtr->ai_family,
addrInfoPtr->ai_socktype, 0 );
if( isBadSocket( netSocket ) )
{
krnlExitMutex( MUTEX_SOCKETPOOL );
return( CRYPT_ERROR_OPEN );
}
socketInfo[ i ].netSocket = netSocket;
if( isServer )
{
const int addrInfoSize = min( addrInfoPtr->ai_addrlen, 32 );
/* Remember the details for this socket so that we can detect another
attempt to bind to it */
socketInfo[ i ].iChecksum = checksumData( addrInfoPtr->ai_addr,
addrInfoPtr->ai_addrlen );
memcpy( socketInfo[ i ].iData, addrInfoPtr->ai_addr,
addrInfoSize );
socketInfo[ i ].iDataLen = addrInfoSize;
}
socketInfo[ i ].refCount = 1;
*newSocketPtr = netSocket;
/* If we're creating a new server socket we can't unlock the socket info
yet because we need to bind it to a port before we do anything else
with it. If we were to unlock the socket info another thread could
perform an accept() on the incompletely set up socket, so we return
with the socket info still locked. When the caller has finished
setting it up they call newSocketDone() to signal that the socket is
now really ready for use */
if( isServer )
return( OK_SPECIAL );
krnlExitMutex( MUTEX_SOCKETPOOL );
return( CRYPT_OK );
}
static void newSocketDone( void )
{
/* The caller has finished setting up a new server socket, unlock the
socket info to allow others to access it */
krnlExitMutex( MUTEX_SOCKETPOOL );
}
CHECK_RETVAL \
static int addSocket( const SOCKET netSocket )
{
int i, status;
REQUIRES( !isBadSocket( netSocket ) );
status = krnlEnterMutex( MUTEX_SOCKETPOOL );
if( cryptStatusError( status ) )
return( status );
/* Add an existing socket entry */
for( i = 0; i < SOCKETPOOL_SIZE; i++ )
{
if( isBadSocket( socketInfo[ i ].netSocket ) )
break;
}
if( i >= SOCKETPOOL_SIZE )
{
krnlExitMutex( MUTEX_SOCKETPOOL );
assert( DEBUG_WARN );
return( CRYPT_ERROR_OVERFLOW );
}
socketInfo[ i ] = SOCKET_INFO_TEMPLATE;
socketInfo[ i ].netSocket = netSocket;
socketInfo[ i ].refCount = 1;
krnlExitMutex( MUTEX_SOCKETPOOL );
return( CRYPT_OK );
}
static void deleteSocket( const SOCKET netSocket )
{
int i, status;
REQUIRES_V( !isBadSocket( netSocket ) );
status = krnlEnterMutex( MUTEX_SOCKETPOOL );
if( cryptStatusError( status ) )
return;
/* Find the entry for this socket in the pool. There may not be one
present if the pool has received a shutdown signal and closed all
network sockets, so if we don't find it we just exit normally */
for( i = 0; i < SOCKETPOOL_SIZE; i++ )
{
if( socketInfo[ i ].netSocket == netSocket )
break;
}
if( i >= SOCKETPOOL_SIZE )
{
krnlExitMutex( MUTEX_SOCKETPOOL );
return;
}
REQUIRES_V( socketInfo[ i ].refCount > 0 );
/* Decrement the socket's reference count */
socketInfo[ i ].refCount--;
if( socketInfo[ i ].refCount <= 0 )
{
/* If the reference count has reached zero, close the socket
and delete the pool entry */
status = closesocket( socketInfo[ i ].netSocket );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -