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

📄 net_tcp.c

📁 提供了很多种加密算法和CA认证及相关服务如CMP、OCSP等的开发
💻 C
📖 第 1 页 / 共 3 页
字号:

	exitMutex( 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) */

void netSignalShutdown( void )
	{
	int i;

	enterMutex( 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 );
			memcpy( &socketInfo[ i ], &socketInfoTemplate,
					sizeof( SOCKET_INFO ) );
			}

	exitMutex( MUTEX_SOCKETPOOL );
	}

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

/* Open and close a connection to a remote server.  This function performs
   that most amazing of all things, the nonblocking connect, this is
   currently done in order to allow a shorter timeout than the default
   fortnight or so but it also allows for two-phase connects in which we
   start the connect operation, perform further processing (eg signing and
   encrypting data prior to sending it over the connected socket) and then
   complete the connect before the first read or write.  Currently we just
   use a wrapper which performs the two back-to-back as a single operation,
   so it only functions as a timeout mechanism */

void closeSocket( STREAM *stream );

static int preOpenSocket( STREAM *stream, const char *server, const int port )
	{
	SOCKET netSocket;
	struct sockaddr_in serverAddr;
	BYTE buffer[ 16 ];
	int status;

	/* Clear return value */
	stream->netSocket = CRYPT_ERROR;

	/* Set up addressing information */
	status = getIPAddress( stream, buffer, server );
	if( cryptStatusError( status ) )
		return( status );
	memset( &serverAddr, 0, sizeof( struct sockaddr_in ) );
	serverAddr.sin_family = AF_INET;
	memcpy( &serverAddr.sin_addr.s_addr, buffer, 4 );
	serverAddr.sin_port = htons( ( in_port_t ) port );

	/* Create a socket, make it nonblocking, and start the connect to the
	   remote server */
	status = newSocket( &netSocket, CRYPT_UNUSED );
	if( cryptStatusError( status ) )
		return( status );
	setSocketNonblocking( netSocket );
	status = connect( netSocket, ( struct sockaddr * ) &serverAddr,
					  sizeof( struct sockaddr_in ) );
	if( status < 0 && !isNonblockWarning() )
		{
		/* There was an error condition other than a notification that the
		   operation hasn't completed yet */
		status = getSocketError( stream, CRYPT_ERROR_OPEN );
		deleteSocket( netSocket );
		return( status );
		}
	if( status == 0 )
		{
		/* If we're connecting to a local host, the connect can complete
		   immediately rather than returning an in-progress status, in
		   which case we don't need to do anything else */
		stream->netSocket = netSocket;
		return( CRYPT_OK );
		}

	/* The connect is in progress, mark the stream as not-quite-ready */
/*	stream->xxx = yyy; */
	stream->netSocket = netSocket;

	return( CRYPT_OK );
	}

static int completeOpen( STREAM *stream )
	{
	struct timeval tv;
	fd_set rfds, wfds;
	int value, length = sizeof( int ), status;

	/* Wait around until the connect completes */
	FD_ZERO( &rfds );
	FD_ZERO( &wfds );
	FD_SET( stream->netSocket, &rfds );
	FD_SET( stream->netSocket, &wfds );
	tv.tv_sec = stream->timeout;
	tv.tv_usec = 0;
	status = select( stream->netSocket + 1, &rfds, &wfds, NULL, &tv );
	if( status == 0 || \
		!( FD_ISSET( stream->netSocket, &rfds ) || \
		   FD_ISSET( stream->netSocket, &wfds ) ) )
		{
		/* We timed out on the connect (status == 0) or we encountered an
		   error condition (the socket is neither readable nor writeable),
		   exit */
		status = getSocketError( stream, CRYPT_ERROR_OPEN );
		if( stream->errorCode == 0 )
			{
			/* Some implementations don't treat a soft timeout as an error
			   so we insert a timeout error code ourselves */
			stream->errorCode = TIMEOUT_ERROR;
			mapError( stream, socketErrorInfo, stream->errorCode,
					  CRYPT_UNUSED );
			}
		closeSocket( stream );
		return( status );
		}

	/* The socket is readable or writeable, however this may be because of
	   an error (it's readable and writeable) or because everything's OK
	   (it's writeable) or because everything's OK and there's data waiting
	   (it's readable and writeable), so we have to see what the error
	   condition is for the socket to determine what's really happening.

	   This is a somewhat tricky area, other possibilities are calling recv()
	   with a length of zero bytes (returns an error if the connect failed),
	   calling connect() again (fails with EISCONN if the connect succeeded),
	   and calling getmsg( netSocket, NULL, NULL, &( flags = 0 ) ) (fails
	   with errno == EAGAIN or EWOULDBLOCK if the only error is that there's
	   nothing available yet) */
	status = getsockopt( stream->netSocket, SOL_SOCKET, SO_ERROR,
						 ( void * ) &value, &length );
	if( status == 0 )
		{
		/* Berkeley-derived implementation, error is in value variable */
		if( value != 0 )
			{
			status = mapError( stream, socketErrorInfo, value,
							   CRYPT_ERROR_OPEN );
			closeSocket( stream );
			return( status );
			}
		}
	else
		/* Solaris, error is in errno */
		if( isSocketError( status ) )
			{
			status = getSocketError( stream, CRYPT_ERROR_OPEN );
			closeSocket( stream );
			return( status );
			}

	/* We've completed the connection, mark the stream as ready for use */
/*	stream->xxx = zzz; */
	return( CRYPT_OK );
	}

static int openServerSocket( STREAM *stream, const char *server, const int port )
	{
	SOCKET listenSocket, netSocket;
	struct sockaddr_in serverAddr, clientAddr;
	char *ipAddress;
	static const int trueValue = 1;
	int clientAddrLen = sizeof( struct sockaddr_in ), status;

	/* Clear return value */
	stream->netSocket = CRYPT_ERROR;

	/* Set up addressing information */
	memset( &serverAddr, 0, sizeof( struct sockaddr_in ) );
	serverAddr.sin_family = AF_INET;
	if( server != NULL )
		{
		BYTE buffer[ 16 ];

		/* We're binding to a specified interface, set up the addresing
		   information */
		status = getIPAddress( stream, buffer, server );
		if( cryptStatusError( status ) )
			return( status );
		memcpy( &serverAddr.sin_addr.s_addr, buffer, 4 );
		}
	else
		/* Allow connections on any interface */
		serverAddr.sin_addr.s_addr = htonl( INADDR_ANY );
	serverAddr.sin_port = htons( ( in_port_t ) port );

	/* Create a new server socket */
	status = newSocket( &listenSocket, port );
	if( cryptStatusError( status ) )
		{
		/* If there was an error creating the socket, don't try anything
		   further */
		if( status != OK_SPECIAL )
			return( status );

		/* This is a new socket, set SO_REUSEADDR to avoid TIME_WAIT
		   problems, and prepare to accept connections */
		if( setsockopt( listenSocket, SOL_SOCKET, SO_REUSEADDR,
						( char * ) &trueValue, sizeof( int ) ) || \
			bind( listenSocket, ( struct sockaddr * ) &serverAddr,
				  sizeof( struct sockaddr_in ) ) || \
			listen( listenSocket, 5 ) )
			{
			status = getSocketError( stream, CRYPT_ERROR_OPEN );
			deleteSocket( listenSocket );
			newSocketDone();
			return( status );
			}

		/* We've finished initialising the socket, tell the socket pool
		   manager that it's safe to let others access the pool */
		newSocketDone();
		}

	/* Wait for a connection */
	netSocket = accept( listenSocket, ( struct sockaddr * ) &clientAddr,
						&clientAddrLen );
	if( isBadSocket( netSocket ) )
		{
		status = getSocketError( stream, CRYPT_ERROR_OPEN );
		deleteSocket( listenSocket );
		return( status );
		}

	/* Get the IP address of the connected client.  We could gets its full
	   name, but this can slow down connections because of the time it takes
	   to do the lookup and is less authoritative because of potential
	   spoofing (in any case the caller can still look up the name if they
	   need it) */
	if( ( ipAddress = inet_ntoa( clientAddr.sin_addr ) ) != NULL )
		{
#if 0
		clientNameInfo = \
				gethostbyaddr( ( void * ) &clientNameInfo.sin_addr.s_addr,
							   sizeof( struct in_addr ), AF_INET );
		strcpy( ..., clientNameInfo->h_name );
#endif /* 0 */
		strncpy( stream->clientAddress, ipAddress,
				 sizeof( stream->clientAddress ) );
		stream->clientAddress[ sizeof( stream->clientAddress ) - 1 ] = '\0';
		stream->clientPort = ntohs( clientAddr.sin_port );
		}

	/* We've got a new connection, add the socket to the pool.  Since this
	   was created externally to the pool, we don't use newSocket() to
	   create a new socket but only add the existing socket */
	status = addSocket( netSocket );
	if( cryptStatusError( status ) )
		{
		/* There was a problem adding the new socket, close it and exit.
		   We don't call deleteSocket() since it wasn't added to the pool,
		   instead we call closesocket() directly */
		closesocket( netSocket );
		return( status );
		}
	stream->netSocket = netSocket;
	stream->listenSocket = listenSocket;

	return( CRYPT_OK );
	}

int openSocket( STREAM *stream, const char *server, const int port )
	{
	int status;

	/* If it's a server stream, open a listening socket */
	if( stream->isServer )
		return( openServerSocket( stream, server, port ) );

	/* It's a client stream, perform a two-part nonblocking open.  Currently
	   the two portions are performed synchronously, in the future we can
	   interleave the two and perform general crypto processing (eg hash/MAC
	   context setup for SSL) while the open is completing */
	status = preOpenSocket( stream, server, port );
	if( cryptStatusOK( status ) )
		status = completeOpen( stream );

	assert( ( cryptStatusError( status ) && \
			  stream->netSocket == CRYPT_ERROR ) || \
			( cryptStatusOK( status ) && \
			  stream->netSocket != CRYPT_ERROR ) );
	return( status );
	}

void closeSocket( STREAM *stream )
	{
	/* If it's an open-on-demand HTTP stream then the socket isn't
	   necessarily open even if the stream was successfully connected, so
	   we only close it if necessary.  It's easier handling it at this level
	   than expecting the caller to distinguish between an opened-stream-but-
	   not-opened-socket and a conventional open stream */
	if( stream->netSocket != CRYPT_ERROR )
		deleteSocket( stream->netSocket );
	if( stream->listenSocket != CRYPT_ERROR )
		deleteSocket( stream->listenSocket );
	stream->netSocket = CRYPT_ERROR;
	}

/* Read and write data from and to a socket.  Because data can appear in
   bits and pieces when reading we have to implement timeout handling at two
   levels, once via select() and a second time as an overall timeout.  If we
   only used select() this could potentially stretch the overall timeout to
   (length * timeout) so we also perform a time check which leads to a worst-
   case timeout of (timeout-1 + timeout) */

int readSocket( STREAM *stream, BYTE *buffer, int length )
	{
	const time_t startTime = time( NULL );
	struct timeval tv;
	fd_set fds;
	BYTE *bufPtr = buffer;
	int byteCount = 0;

	while( length > 0 && \
		   ( time( NULL ) - startTime < stream->timeout || !stream->timeout ) )
		{
		int bytesRead, status;

		/* Set up the information needed to handle timeouts.  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, so we
		   reset it each time just to be on the safe side */
		FD_ZERO( &fds );
		FD_SET( stream->netSocket, &fds );
		tv.tv_sec = stream->timeout;
		tv.tv_usec = 0;

		/* See if there's anything available */
		status = select( stream->netSocket + 1, &fds, NULL, NULL, &tv );
		if( isSocketError( status ) )
			return( getSocketError( stream, CRYPT_ERROR_READ ) );
		if( status == 0 )
			{
			if( !stream->timeout )
				/* If it's a nonblocking read then the unavailability of data
				   isn't an error */
				return( 0 );
			return( setSocketError( stream, "Timeout on read",
									CRYPT_ERROR_READ ) );
			}

		/* We've got data waiting, read it */
		bytesRead = recv( stream->netSocket, bufPtr, length, 0 );
		if( isSocketError( bytesRead ) )
			return( getSocketError( stream, CRYPT_ERROR_READ ) );
		if( bytesRead == 0 )
			{
			/* "It said its piece, and then it sodded off" - Baldrick,
			   Blackadder's Christmas Carol*/
			length = 0;
			break;
			}
		bufPtr += bytesRead;
		length -= bytesRead;
		byteCount += bytesRead;
		}
	if( length || !byteCount )
		/* We timed out before reading all the data or we didn't get
		   anything because the other side closed the connection */
		return( setSocketError( stream, !byteCount ? "No data read because "
								"remote system has closed connection" : \
								"Timeout on read", CRYPT_ERROR_READ ) );

	return( byteCount );
	}

int writeSocket( STREAM *stream, const BYTE *buffer, int length )
	{
	int status;

	status = send( stream->netSocket, buffer, length, 0 );
	if( status < length )
		return( getSocketError( stream, status ) );

	return( CRYPT_OK );
	}
#endif /* NET_TCP */

⌨️ 快捷键说明

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