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

📄 tcp.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 5 页
字号:
			}
		if( writeFDPtr != NULL )
			{
			FD_ZERO( writeFDPtr );
			FD_SET( stream->netSocket, writeFDPtr );
			}
		FD_ZERO( &exceptfds );
		FD_SET( stream->netSocket, &exceptfds );
		tv.tv_sec = timeout;
		tv.tv_usec = ( timeout <= 0 ) ? 5000 : 0;

		/* See if we can perform the I/O */
		status = select( stream->netSocket + 1, readFDPtr, writeFDPtr,
						 &exceptfds, &tv );

		/* If there's a problem and it's not something transient like an
		   interrupted system call, exit.  For a transient problem, we just
		   retry the select until the overall timeout expires */
		if( isSocketError( status ) && !isRestartableError() )
			return( getSocketError( stream, errorInfo[ type ].status ) );
		}
	while( isSocketError( status ) && ( getTime() - startTime ) < timeout );

	/* If the wait timed out, either explicitly in the select (status == 0)
	   or implicitly in the wait loop (isSocketError()), report it as a
	   select() timeout error */
	if( status == 0 || isSocketError( status ) )
		{
		char errorMessage[ 128 ];

		/* If we've already received data from a previous I/O, it counts as
		   the transferred byte count even though we timed out this time
		   round */
		if( currentByteCount > 0 )
			return( currentByteCount );

		/* If it's a nonblocking wait (usually used as a poll to determine
		   whether I/O is possible) then a timeout isn't an error */
		if( timeout <= 0 )
			return( OK_SPECIAL );

		/* The select() timed out, exit */
		sPrintf( errorMessage, "Timeout on %s (select()) after %d seconds",
				 errorInfo[ type ].errorString, timeout );
		return( setSocketError( stream, errorMessage, CRYPT_ERROR_TIMEOUT,
								FALSE ) );
		}

#if 0	/* 12/6/04 Shouldn't be necessary any more since to get to this
		   point the socket has to be either readable or writeable or
		   subject to an exception condition, which is handled below */
	/* If we encountered an error condition on a connect (the socket is
	   neither readable nor writeable), exit */
	if( ( type == IOWAIT_CONNECT ) && \
		!( FD_ISSET( stream->netSocket, &readfds ) || \
		   FD_ISSET( stream->netSocket, &writefds ) ) )
		{
		assert( FD_ISSET( stream->netSocket, &exceptfds ) );

		status = getSocketError( stream, CRYPT_ERROR_OPEN );
		if( stream->errorCode == 0 )
			{
			/* Some implementations don't treat a soft timeout as an error,
			   and at least one (Tandem) returns EINPROGRESS rather than
			   ETIMEDOUT, so we insert a timeout error code ourselves */
			stream->errorCode = TIMEOUT_ERROR;
			mapError( stream, socketErrorInfo, CRYPT_UNUSED );
			}
		return( status );
		}
#endif /* 0 */

	/* If there's an exception condition on a socket, exit.  This is
	   implementation-specific, traditionally under Unix this only indicates
	   the arrival of out-of-band data rather than any real error condition,
	   but in some cases it can be used to signal errors.  In these cases we
	   have to explicitly check for an exception condition because some
	   types of errors will result in select() timing out waiting for
	   readability, rather than indicating an error.  In addition for OOB
	   data we could just ignore the notification (which happens
	   automatically with the default setting of SO_OOBINLINE = false and a
	   socket owner to receive SIGURG's not set, the OOB data byte just
	   languishes in a side-buffer), however we shouldn't be receiving OOB
	   data so we treat it as an error */
	if( FD_ISSET( stream->netSocket, &exceptfds ) )
		{
		status = getSocketError( stream, errorInfo[ type ].status );
		if( stream->errorCode == 0 )
			{
			/* If there's a (supposed) exception condition present but no
			   error information available then this may be a mis-handled
			   select() timeout.  This can happen under Winsock under
			   certain circumstances, and seems to be related to another
			   app performing network I/O at the same time as we do the
			   wait.  Non-Winsock cases can occur because some
			   implementations don't treat a soft timeout as an error, and
			   at least one (Tandem) returns EINPROGRESS rather than
			   ETIMEDOUT, so we insert a timeout error code ourselves */
			stream->errorCode = TIMEOUT_ERROR;
			mapError( stream, socketErrorInfo, CRYPT_UNUSED );
			}
		return( status );
		}

	/* The socket is read for reading or writing */
	assert( status > 0 );
	assert( ( type == IOWAIT_READ && \
			  FD_ISSET( stream->netSocket, &readfds ) ) || \
			( type == IOWAIT_WRITE && \
			  FD_ISSET( stream->netSocket, &writefds ) ) || \
			( type == IOWAIT_CONNECT && \
			  ( FD_ISSET( stream->netSocket, &readfds ) || \
				FD_ISSET( stream->netSocket, &writefds ) ) ) || \
			( type == IOWAIT_ACCEPT ) );
	return( CRYPT_OK );
	}

/* Open a connection to a remote server/wait for a connection from a remote
   client.  The connection-open 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 (e.g. 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 that
   performs the two back-to-back as a single operation, so it only functions
   as a timeout-management mechanism */

static int preOpenSocket( STREAM *stream, const char *server,
						  const int serverPort )
	{
	SOCKET netSocket;
	struct addrinfo *addrInfoPtr, *addrInfoCursor;
	BOOLEAN nonBlockWarning = FALSE;
	int port = serverPort, socketStatus, status;

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

	/* Set up addressing information */
	status = getAddressInfo( stream, &addrInfoPtr, server, port, FALSE );
	if( cryptStatusError( status ) )
		return( status );

	/* Create a socket, make it nonblocking, and start the connect to the
	   remote server, falling back through alternative addresses if the
	   connect fails.  Since this is a nonblocking connect it could still
	   fail during the second phase where we can no longer try to recover
	   by falling back to an alternative address, but it's better than just
	   giving up after the first address we try */
	for( addrInfoCursor = addrInfoPtr; addrInfoCursor != NULL;
		 addrInfoCursor = addrInfoCursor->ai_next )
		{
		status = newSocket( &netSocket, addrInfoCursor, FALSE );
		if( cryptStatusError( status ) )
			{
			/* We need to get the socket error code now because further
			   calls to functions such as freeaddrinfo() will overwrite
			   the global error value before we can read it later on */
			socketStatus = getErrorCode();
			continue;
			}
		setSocketNonblocking( netSocket );
		status = connect( netSocket, addrInfoCursor->ai_addr,
						  addrInfoCursor->ai_addrlen );
		nonBlockWarning = isNonblockWarning();
		if( status >= 0 || nonBlockWarning )
			/* We've got a successfully-started connect, exit */
			break;
		socketStatus = getErrorCode();	/* Remember socket error code */
		deleteSocket( netSocket );
		}
	freeAddressInfo( addrInfoPtr );
	if( status < 0 && !nonBlockWarning )
		{
		/* There was an error condition other than a notification that the
		   operation hasn't completed yet */
		status = mapError( stream, socketErrorInfo, 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 )
	{
	static const int trueValue = 1;
	SIZE_TYPE intLength = sizeof( int );
	int value, status;

	/* Wait around until the connect completes.  Some select()s limit the
	   size of the second count, so we set it to a maximum of 1 year's worth.
	   BeOS doesn't allow setting a timeout (that is, it doesn't allow
	   asynchronous connects), but it hardcodes in a timeout of about a
	   minute so we get a vaguely similar effect */
	status = ioWait( stream, min( stream->timeout, 30000000L ), 0,
					 IOWAIT_CONNECT );
	if( cryptStatusError( status ) )
		{
		stream->transportDisconnectFunction( stream, TRUE );
		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, &intLength );
	if( status == 0 )
		{
		/* Berkeley-derived implementation, error is in value variable */
		if( value != 0 )
			{
			status = mapError( stream, socketErrorInfo, CRYPT_ERROR_OPEN );
			stream->transportDisconnectFunction( stream, TRUE );
			return( status );
			}
		}
	else
		/* Slowaris, error is in errno */
		if( isSocketError( status ) )
			{
			status = getSocketError( stream, CRYPT_ERROR_OPEN );
			stream->transportDisconnectFunction( stream, TRUE );
			return( status );
			}

	/* Turn off Nagle (since we do our own optimised TCP handling) and make
	   the socket blocking again.  This is necessary because with a
	   nonblocking socket Winsock will occasionally return 0 bytes from
	   recv() (a sign that the receiver has closed the connection, see the
	   comment in readSocketFunction()) even though the connection is still
	   fully open, and in any case there's no real need for a nonblocking
	   socket since we have select() handling timeouts/blocking for us */
	setsockopt( stream->netSocket, IPPROTO_TCP, TCP_NODELAY,
				( void * ) &trueValue, sizeof( int ) );
	setSocketBlocking( stream->netSocket );

	/* 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;
	SOCKADDR_STORAGE clientAddr;
	struct addrinfo *addrInfoPtr, *addrInfoCursor;
	static const int trueValue = 1;
	SIZE_TYPE clientAddrLen = sizeof( SOCKADDR_STORAGE );
	int socketStatus, status;

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

	/* Set up addressing information.  If we're not binding to a specified
	   interface, we allow connections on any interface.  Note that, in
	   combination with SO_REUSEADDR and older, unpatched kernels, this
	   allows port hijacking by another process running on the same machine
	   that binds to the port with a more specific binding than "any" */
	status = getAddressInfo( stream, &addrInfoPtr, server, port, TRUE );
	if( cryptStatusError( status ) )
		return( status );

	/* Create a new server socket, falling back through alternative
	   interfaces if the initial socket creation fails.  This may seem less
	   necessary than for the client-side connect, but is in fact required
	   because getaddrinfo() usually preferentially provides an IPv6
	   interface even if there's no IPv6 configured for the system (see the
	   long comment in getAddressInfo() for more on this), so we have to
	   step through until we get to an IPv4 interface, or at least one that
	   we can listen on */
	for( addrInfoCursor = addrInfoPtr; addrInfoCursor != NULL;
		 addrInfoCursor = addrInfoCursor->ai_next )
		{
		status = newSocket( &listenSocket, addrInfoCursor, TRUE );
		if( status == CRYPT_OK )
			/* It's a second thread listening on an existing socket,
			   we're done */
			break;
		if( status != OK_SPECIAL )
			{
			/* There was a problem creating the socket, try again with
			   another interface.  We need to get the socket error code now
			   because further calls to functions such as freeaddrinfo()
			   will overwrite the global error value before we can read it
			   later on */
			socketStatus = getErrorCode();
			continue;
			}
		status = CRYPT_OK;

		/* This is a new socket, set SO_REUSEADDR to avoid TIME_WAIT
		   problems, and prepare to accept connections (nemo surdior est
		   quam is qui non audiet).  Note that BeOS can only bind to one
		   interface at a time, so if we're binding to INADDR_ANY under
		   BeOS we actually bind to the first interface that we find */
		if( setsockopt( listenSocket, SOL_SOCKET, SO_REUSEADDR,
						( char * ) &trueValue, sizeof( int ) ) || \
			bind( listenSocket, addrInfoCursor->ai_addr,
				  addrInfoCursor->ai_addrlen ) || \
			listen( listenSocket, 5 ) )
			{
			socketStatus = getErrorCode();	/* Remember socket error code */
			deleteSocket( listenSocket );
			newSocketDone();
			continue;
			}

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

⌨️ 快捷键说明

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