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

📄 tcp.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 5 页
字号:
			 ( type == IOWAIT_ACCEPT ) );
	return( CRYPT_OK );
	}

/* Open a connection to a remote server or 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 for now it only 
   functions as a timeout-management mechanism - the high-level API for 
   this would be a bit difficult to handle since there's no readily-
   available facility for handling an interruptible sNetConnect(), the best 
   option would be to handle it via a complete-connect IOCTL.  However since
   we've got at least a little time to play with in most cases we could at
   least perform a quick entropy poll in the idle interval, if nothing 
   else */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int preOpenSocket( INOUT NET_STREAM_INFO *netStream, 
						  IN_BUFFER( hostNameLen ) const char *host, 
						  IN_LENGTH_DNS const int hostNameLen,
						  IN_PORT const int port )
	{
	SOCKET netSocket = DUMMY_INIT;
	struct addrinfo *addrInfoPtr, *addrInfoCursor;
	BOOLEAN nonBlockWarning = FALSE;
	int socketStatus, addressCount, status;

	assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
	assert( isReadPtr( host, hostNameLen ) );
	
	REQUIRES( hostNameLen > 0 && hostNameLen <= MAX_DNS_SIZE );
	REQUIRES( port >= 22 && port < 65536L );

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

	/* Set up addressing information */
	status = getAddressInfo( netStream, &addrInfoPtr, host, hostNameLen, 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 that we try */
	for( addrInfoCursor = addrInfoPtr, addressCount = 0;
		 addrInfoCursor != NULL && addressCount < IP_ADDR_COUNT;
		 addrInfoCursor = addrInfoCursor->ai_next, addressCount++ )
		{
		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 );
		}
	if( addressCount >= IP_ADDR_COUNT )
		{
		/* We went through a suspiciously large number of remote server 
		   addresses without being able to even initiate a connect attempt 
		   to any of them, there's something wrong */
		assert( DEBUG_WARN );
		return( mapError( netStream, FALSE, CRYPT_ERROR_OPEN ) );
		}
	freeAddressInfo( addrInfoPtr );
	if( status < 0 && !nonBlockWarning )
		{
		/* There was an error condition other than a notification that the
		   operation hasn't completed yet */
		return( mapError( netStream, FALSE, CRYPT_ERROR_OPEN ) );
		}
	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 */
		netStream->netSocket = netSocket;
		return( CRYPT_OK );
		}

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

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int completeOpen( INOUT NET_STREAM_INFO *netStream )
	{
	static const int trueValue = 1;
	SIZE_TYPE intLength = sizeof( int );
	int value, status;

	assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
	assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );

	/* Wait around until the connect completes.  Some select()s limit the
	   size of the second count so we set it to a maximum of about a week,
	   although why anyone would wait around that long (and whether any
	   network stack would even maintain a SYN_SENT for that amount of time)
	   is unclear.
	   
	   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( netStream, min( netStream->timeout, 500000L ), FALSE,
					 IOWAIT_CONNECT );
	if( cryptStatusError( status ) )
		{
		netStream->transportDisconnectFunction( netStream, 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.

	   How to best determine all of these conditions is a bit tricky.  Other 
	   possibilities include 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), but these are somewhat implementation-
	   specific and not consistent across different platforms */
	status = getsockopt( netStream->netSocket, SOL_SOCKET, SO_ERROR,
						 ( void * ) &value, &intLength );
	if( status == 0 )
		{
		/* Berkeley-derived implementation, error is in value variable */
		if( value != 0 )
			{
			status = mapError( netStream, FALSE, CRYPT_ERROR_OPEN );
			netStream->transportDisconnectFunction( netStream, TRUE );
			return( status );
			}
		}
	else
		{
		/* Slowaris, error is in errno */
		if( isSocketError( status ) )
			{
			status = getSocketError( netStream, CRYPT_ERROR_OPEN );
			netStream->transportDisconnectFunction( netStream, 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 other side 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( netStream->netSocket, IPPROTO_TCP, TCP_NODELAY,
				( void * ) &trueValue, sizeof( int ) );
	setSocketBlocking( netStream->netSocket );

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

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int openServerSocket( INOUT NET_STREAM_INFO *netStream, 
							 IN_BUFFER_OPT( hostNameLen ) const char *host, 
							 IN_LENGTH_DNS const int hostNameLen,
							 IN_PORT const int port )
	{
	SOCKET listenSocket = DUMMY_INIT, netSocket;
	SOCKADDR_STORAGE clientAddr;
	struct addrinfo *addrInfoPtr, *addrInfoCursor;
	static const int trueValue = 1;
	SIZE_TYPE clientAddrLen = sizeof( SOCKADDR_STORAGE );
	char hostNameBuffer[ MAX_DNS_SIZE + 1 + 8 ];
	int socketStatus, addressCount, status = CRYPT_ERROR_OPEN;

	assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
	assert( ( host == NULL && hostNameLen == 0 ) || \
			isReadPtr( host, hostNameLen ) );

	REQUIRES( ( host == NULL && hostNameLen == 0 ) || \
			  ( host != NULL && \
				hostNameLen > 0 && hostNameLen <= MAX_DNS_SIZE ) );
	REQUIRES( port >= 22 && port < 65536L );

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

	/* Convert the host name into the null-terminated string required by the 
	   sockets API if necessary */
	if( host != NULL )
		{
		REQUIRES( hostNameLen > 0 && hostNameLen < MAX_DNS_SIZE );

		memcpy( hostNameBuffer, host, hostNameLen );
		hostNameBuffer[ hostNameLen ] = '\0';
		host = hostNameBuffer;
		}

	/* 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 old 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( netStream, &addrInfoPtr, host, hostNameLen, 
							 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 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.  
	   Qui habet aures audiendi audiat (the speaker appears to be speaking 
	   metaphorically with 'ears' referring to 'network sockets', latin 
	   having no native term for the latter) */
	for( addrInfoCursor = addrInfoPtr, addressCount = 0; 
		 addrInfoCursor != NULL && addressCount < IP_ADDR_COUNT;
		 addrInfoCursor = addrInfoCursor->ai_next, addressCount++ )
		{
		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;

		/* At this point we still have the socket pool locked while we 
		   complete initialisation so we need to call newSocketDone()
		   before we break out of the loop at any point */

		/* 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;
		}
	freeAddressInfo( addrInfoPtr );
	if( addressCount >= IP_ADDR_COUNT )
		{
		/* We went through a suspiciously large number of server addresses 
		   without being able to even initiate a listen attempt on any of 
		   them, there's something wrong */
		assert( DEBUG_WARN );
		return( mapError( netStream, FALSE, CRYPT_ERROR_OPEN ) );
		}
	if( cryptStatusError( status ) )
		{
		/* There was an error setting up the socket, don't try anything
		   further */
		return( mapError( netStream, FALSE, CRYPT_ERROR_OPEN ) );
		}

	/* Wait for a connection.  At the moment this always waits forever
	   (actually some select()s limit the size of the second count so we
	   set it to a maximum of 1 year's worth), but in the future we could
	   have a separate timeout value for accepting incoming connections to
	   mirror the connection-wait timeout for outgoing connections.

	   Because of the way that accept works, the socket that we eventually
	   and up with isn't the one that we listen on, but we have to
	   temporarily make it the one associated with the stream in order for
	   ioWait() to work */
	netStream->netSocket = listenSocket;
	status = ioWait( netStream, min( netStream->timeout, 30000000L ), FALSE,
					 IOWAIT_ACCEPT );
	netStream->netSocket = CRYPT_ERROR;
	if( cryptStatusError( status ) )
		return( status );

	/* We have an incoming connection ready to go, accept it.  There's a
	   potential complication here in that if a client connects and then
	   immediately sends a RST after the TCP handshake has completed,
	   ioWait() will return with an indication that there's an incoming
	   connection ready to go but the following accept(), if it's called
	   after the RST has arrived, will block waiting for the next incoming
	   connection.  This is rather unlikely in practice, but could occur
	   as part of a DoS by setting the

⌨️ 快捷键说明

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