⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:

	/* 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 + -