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

📄 tcp.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 5 页
字号:
   dynamically expands its object table */

#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 ];		/*	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 */

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

static int newSocket( SOCKET *newSocketPtr, struct addrinfo *addrInfoPtr,
					  const BOOLEAN isServer )
	{
	SOCKET netSocket;
	int i;

	/* Clear return value */
	*newSocketPtr = INVALID_SOCKET;

	krnlEnterMutex( MUTEX_SOCKETPOOL );

	/* 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 reorganisation of
	   socket functions for IPv6 */
	if( isServer )
		{
		const int iCheck = checksumData( addrInfoPtr->ai_addr,
										 addrInfoPtr->ai_addrlen );

		for( i = 0; i < SOCKETPOOL_SIZE; i++ )
			if( socketInfo[ i ].iChecksum == iCheck && \
				socketInfo[ i ].iDataLen == addrInfoPtr->ai_addrlen && \
				!memcmp( socketInfo[ i ].iData, addrInfoPtr->ai_addr,
						 addrInfoPtr->ai_addrlen ) )
				{
				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 && \
			socketInfo[ i ].netSocket != INVALID_SOCKET )
			{
			int status;

			status = closesocket( socketInfo[ i ].netSocket );
			if( !isSocketError( status ) )
				socketInfo[ i ] = SOCKET_INFO_TEMPLATE;
			}

		if( socketInfo[ i ].netSocket == INVALID_SOCKET )
			break;
		}
	if( i >= SOCKETPOOL_SIZE )
		{
		krnlExitMutex( MUTEX_SOCKETPOOL );
		assert( NOTREACHED );
		return( CRYPT_ERROR_OVERFLOW );	/* Should never happen */
		}
	if( isBadSocket( netSocket = socket( addrInfoPtr->ai_family,
										 addrInfoPtr->ai_socktype, 0 ) ) )
		{
		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 */
		assert( addrInfoPtr->ai_addrlen <= 32 );
		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'll call newSocketDone() to signal that the socket
	   is 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 );
	}

static int addSocket( const SOCKET netSocket )
	{
	int i;

	krnlEnterMutex( MUTEX_SOCKETPOOL );

	/* Add an existing socket entry */
	for( i = 0; i < SOCKETPOOL_SIZE; i++ )
		if( socketInfo[ i ].netSocket == INVALID_SOCKET )
			break;
	if( i >= SOCKETPOOL_SIZE )
		{
		krnlExitMutex( MUTEX_SOCKETPOOL );
		assert( NOTREACHED );
		return( CRYPT_ERROR_OVERFLOW );	/* Should never happen */
		}
	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;

	krnlEnterMutex( MUTEX_SOCKETPOOL );

	/* 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 exit normally */
	for( i = 0; i < SOCKETPOOL_SIZE; i++ )
		if( socketInfo[ i ].netSocket == netSocket )
			break;
	if( i >= SOCKETPOOL_SIZE )
		{
		krnlExitMutex( MUTEX_SOCKETPOOL );
		return;
		}
	assert( socketInfo[ i ].refCount > 0 );

	/* Decrement the socket's reference count */
	socketInfo[ i ].refCount--;
	if( socketInfo[ i ].refCount <= 0 )
		{
		int status;

		/* If the reference count has reached zero, close the socket
		   and delete the pool entry */
		status = closesocket( socketInfo[ i ].netSocket );
		if( isSocketError( status ) )
			{
			/* There was a problem closing the socket, mark it as not-
			   present for matching purposes but keep its entry active so
			   that we'll periodically try and close it when we search the
			   socket pool for these slots, and again when we close down */
			socketInfo[ i ].iChecksum = 0;
			memset( socketInfo[ i ].iData, 0,
					sizeof( socketInfo[ i ].iData ) );
			socketInfo[ i ].iDataLen = 0;

			assert( NOTREACHED );
			}
		else
			socketInfo[ i ] = SOCKET_INFO_TEMPLATE;
		}

	krnlExitMutex( MUTEX_SOCKETPOOL );
	}

/* Force all objects waiting on sockets to exit by closing their sockets.
   This is the only way to cause them to terminate, since an object waiting
   on a socket is marked as busy by the cryptlib kernel (and in fact will be
   blocked inside the OS out of reach of even the cryptlib kernel).
   Alternatively, the user can provide their own socket externally and close
   it from the outside, which will unblock the thread waiting on it.

   A somewhat less drastic alternative to closing the socket is to use
   shutdown(), but the behaviour of this is somewhat implementation-specific.
   For example under Slowaris 5.x trying to shutdown a listening socket (to
   unlock a thread blocking in accept()) returns ENOTCONN, so the shutdown
   requires setting up a dummy connection to the socket to be shut down
   before it can actually be shut down.  Trying to shut down a thread blocked
   in connect() is more or less impossible under Slowaris 5.x.  Other systems
   are more flexible, but there's not enough consistency to rely on this */

void netSignalShutdown( void )
	{
	int i;

	krnlEnterMutex( MUTEX_SOCKETPOOL );

	/* For each open socket, close it and set its reference count to zero */
	for( i = 0; i < SOCKETPOOL_SIZE; i++ )
		if( socketInfo[ i ].netSocket != INVALID_SOCKET )
			{
			closesocket( socketInfo[ i ].netSocket );
			socketInfo[ i ] = SOCKET_INFO_TEMPLATE;
			}

	krnlExitMutex( MUTEX_SOCKETPOOL );
	}

/****************************************************************************
*																			*
*							Network Socket Interface						*
*																			*
****************************************************************************/

/* Wait for I/O to become possible on a socket */

typedef enum { IOWAIT_READ, IOWAIT_WRITE, IOWAIT_CONNECT,
			   IOWAIT_ACCEPT } IOWAIT_TYPE;

static int ioWait( STREAM *stream, const time_t timeout,
				   const int currentByteCount, const IOWAIT_TYPE type )
	{
	static const struct {
		const int status;
		const char *errorString;
		} errorInfo[] = {
		{ CRYPT_ERROR_READ, "read" },
		{ CRYPT_ERROR_WRITE, "write" },
		{ CRYPT_ERROR_OPEN, "connect" },
		{ CRYPT_ERROR_OPEN, "accept" },
		{ CRYPT_ERROR_OPEN, "unknown" }
		};
	const time_t startTime = getTime();
	struct timeval tv;
	fd_set readfds, writefds, exceptfds;
	fd_set *readFDPtr = ( type == IOWAIT_READ || \
						  type == IOWAIT_CONNECT || \
						  type == IOWAIT_ACCEPT ) ? &readfds : NULL;
	fd_set *writeFDPtr = ( type == IOWAIT_WRITE || \
						   type == IOWAIT_CONNECT ) ? &writefds : NULL;
	int status;

	/* Set up the information needed to handle timeouts and wait on the
	   socket.  If there's no timeout, we wait at least 5ms on the theory
	   that it isn't noticeable to the caller but ensures that we at least
	   get a chance to get anything that may be pending.

	   The exact wait time depends on the system, but usually it's quantised
	   to the system timer quantum.  This means that on Unix systems with a
	   1ms timer resolution, the wait time is quantised on a 1ms boundary.
	   Under Windows NT/2000/XP, it's quantised on a 10ms boundary (some
	   early NT systems had a granularity ranging from 7.5 - 15ms, but all
	   newer systems use 10ms) and for Win95/98/ME it's quantised on a 55ms
	   boundary.  In other words when performing a select() on a Win95 box
	   it'll either return immediately or wait some multiple of 55ms, even
	   with the time set to 1ms.

	   In theory we shouldn't have to reset either the fds or the timeval
	   each time through the loop since we're only waiting on one descriptor
	   so it's always set and the timeval is a const, however some versions
	   of Linux can update it if the select fails due to an EINTR (which is
	   the exact reason why we'd be going through the loop a second time)
	   and/or if a file descriptor changes status (e.g. due to data becoming
	   available) so we have to reset it each time to be on the safe side.

	   The wait on connect is a slightly special case, the socket will
	   become writeable if the connect succeeds normally, but both readable
	   and writeable if there's an error on the socket or if there's data
	   already waiting on the connection (i.e. it arrives as part of the
	   connect).  It's up to the caller to check for these conditions */
	do
		{
		if( readFDPtr != NULL )
			{
			FD_ZERO( readFDPtr );
			FD_SET( stream->netSocket, readFDPtr );

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -