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

📄 http_rd.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 2 页
字号:
		/* Read the response header */
		status = readFirstHeaderLine( stream, lineBuffer, lineBufSize,
									  &httpStatus );
		if( cryptStatusError( status ) )
			{
			/* Some errors like an HTTP 404 aren't necessarily fatal in the 
			   same way as (say) a CRYPT_ERROR_BADDATA because while the 
			   latter means that the stream has been corrupted and we can't 
			   continue, the former merely means that the requested item 
			   wasn't found but we can still submit further requests.  If 
			   the caller has indicated that they want certain errors to be
			   treated as nonfatal, continue processing the stream and then
			   report the error later */
			if( httpDataInfo->softErrors && httpStatus == 404 )
				isSoftError = TRUE;

			if( status != OK_SPECIAL && !isSoftError )
				return( status );

			/* It's a special-case header (e.g. a 100 Continue), turn the 
			   read into a no-op read that drains the input to get to the 
			   real data */
			*flags |= HTTP_FLAG_NOOP;
			needsSpecialHandling = TRUE;
			}

		/* Process the remaining header lines.  5 bytes is the minimum-size
		   object that can be returned from any HTTP-based message which is
		   exchanged by cryptlib, this being an OCSP response containing a
		   single-byte status value, i.e. SEQUENCE { ENUM x }.

		   If the read buffer is dynamically allocated then we allow an
		   effectively arbitrary content length, otherwise it has to fit 
		   into the fixed-size read buffer.  Unfortunately since CRLs can 
		   reach > 100MB in size it's not really possible to provide any 
		   sensible limit on the length for dynamic-buffer reads, however
		   to avoid DoS issues we limit it to 8MB until someone complains 
		   that they can't read the 150MB CRLs that their CA is issuing 
		   (yes, there are CAs that are issuing 150MB CRLs) */
		initHeaderInfo( &headerInfo, 5,
						httpDataInfo->bufferResize ? \
							min( MAX_INTLENGTH, 8388608L ) : \
							httpDataInfo->bufSize,
						*flags );
		status = readHeaderLines( stream, lineBuffer, lineBufSize,
								  &headerInfo );
		if( cryptStatusError( status ) )
			return( status );

		/* Copy any status info back to the caller */
		*flags = headerInfo.flags & ~HTTP_FLAG_NOOP;
		httpDataInfo->bytesAvail = headerInfo.contentLength;

		/* If this was a soft error due to not finding the requested item, 
		   pass the status on to the caller.  The low-level error
		   information will still be present from readFirstHeaderLine() */
		if( isSoftError )
			return( CRYPT_ERROR_NOTFOUND );

		/* If it's not something like a redirect that needs special-case
		   handling, we're done */
		if( !needsSpecialHandling )
			return( CRYPT_OK );

		REQUIRES( httpStatus == 100 || httpStatus == 301 || \
				  httpStatus == 302 || httpStatus == 307 );

		/* If we got a 100 Continue response, try for another header that
		   follows the first one */
		if( httpStatus == 100 )
			continue;

		/* If we got a 301, 302, or 307 Redirect then in theory we should
		   proceed roughly as per the code below, however in practice it's
		   not nearly as simple as this, because what we're in effect doing
		   is taking a stream and replacing it with a completely new stream
		   (different host/abs-path/query info, new socket with optional
		   proxy handling, etc etc).  One way to do this would be to read
		   the new location into the current stream buffer and pass it back
		   with a special status telling the stream-level code to create a
		   new stream, clean up the old one, and perform a deep copy of the
		   new stream over to the old one.  We'll leave this for a time when
		   it's really needed.

		   In addition the semantics of the following pseudocode don't quite
		   match those of RFC 2616 because of the HTTP-as-a-substrate use
		   rather than direct use in a browser.  Specifically, anything
		   other than a GET for a 302 or 307 isn't supposed to perform an
		   automatic redirect without asking the user, because of concerns
		   that it'll change the semantics of the request.  However since
		   we're not an interactive web browser there's no way that we can
		   ask a user for redirect permission, and in any case since we're
		   merely using HTTP as a substrate for a cryptographically
		   protected PKI message (and specifically assuming that the HTTP
		   layer is completely insecure), any problems will be caught by the
		   crypto protocol layer */
#if 0
		if( !*location )
			return( CRYPT_ERROR_READ );
		netStream->closeSocketFunction( stream );
		clFree( "readResponseHeader", netStream->host );
		netStream->host = NULL;
		status = parseLocation( stream, location );
		if( cryptStatusError( status ) )
			return( CRYPT_ERROR_READ );
#endif /* 0 */
		retExt( CRYPT_ERROR_READ,
				( CRYPT_ERROR_READ, NETSTREAM_ERRINFO, 
				  "Unable to process HTTP %d redirect", httpStatus ) );
		}

	/* We used up our maximum number of retries, bail out */
	retExt( CRYPT_ERROR_READ,
			( CRYPT_ERROR_READ, NETSTREAM_ERRINFO, 
			  "HTTP retry/redirection loop detected" ) );
	}

/****************************************************************************
*																			*
*							HTTP Access Functions							*
*																			*
****************************************************************************/

/* Read data from an HTTP stream */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
static int readFunction( INOUT STREAM *stream, 
						 OUT_BUFFER( maxLength, *length ) void *buffer, 
						 IN_LENGTH_FIXED( sizeof( HTTP_DATA_INFO ) ) \
							const int maxLength, 
						 OUT_LENGTH_Z int *length )
	{
	NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
	HTTP_DATA_INFO *httpDataInfo = ( HTTP_DATA_INFO * ) buffer;
	char headerBuffer[ HTTP_LINEBUF_SIZE + 8 ];
	int flags = HTTP_FLAG_NONE, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( buffer, maxLength ) );
	assert( isWritePtr( length, sizeof( int ) ) );

	REQUIRES( maxLength == sizeof( HTTP_DATA_INFO ) );

	/* Clear return value */
	*length = 0;

	/* Read the HTTP packet header */
	if( netStream->nFlags & STREAM_NFLAG_ISSERVER )
		{
		assert( !( netStream->nFlags & STREAM_NFLAG_HTTPGET ) || \
				isWritePtr( httpDataInfo->reqInfo, \
							sizeof( HTTP_URI_INFO ) ) );

		status = readRequestHeader( stream, headerBuffer, HTTP_LINEBUF_SIZE,
									httpDataInfo, &flags );
		}
	else
		{
		status = readResponseHeader( stream, headerBuffer, HTTP_LINEBUF_SIZE,
									 httpDataInfo, &flags );
		if( cryptStatusOK( status ) && \
			httpDataInfo->bytesAvail > httpDataInfo->bufSize )
			{
			void *newBuffer;

			/* readResponseHeader() will only allow content larger than the 
			   buffer size if it's marked as a resizeable buffer */
			REQUIRES( httpDataInfo->bufferResize );

			/* Adjust the read buffer size to handle the extra data and 
			   record the details of the resized buffer */
			if( ( newBuffer = clAlloc( "readFunction", \
										httpDataInfo->bytesAvail ) ) == NULL )
				return( CRYPT_ERROR_MEMORY );
			zeroise( httpDataInfo->buffer, httpDataInfo->bufSize );
			clFree( "readFunction", httpDataInfo->buffer );
			httpDataInfo->buffer = newBuffer;
			httpDataInfo->bufSize = httpDataInfo->bytesAvail;
			}
		else
			{
			/* We didn't dynically resize the buffer, let the caller know */
			httpDataInfo->bufferResize = FALSE;
			}
		}
	if( cryptStatusError( status ) )
		return( status );
	ENSURES( httpDataInfo->bytesAvail <= httpDataInfo->bufSize );

	REQUIRES( !( netStream->nFlags & STREAM_NFLAG_ISSERVER ) || \
			  ( httpDataInfo->reqType != STREAM_HTTPREQTYPE_NONE ) );

	/* If we're the server and the client sends us an HTTP GET, all of the 
	   information was contained in the header and we're done */
	if( ( netStream->nFlags & STREAM_NFLAG_ISSERVER ) && \
		( httpDataInfo->reqType == STREAM_HTTPREQTYPE_GET ) )
		{
		*length = maxLength;
		return( CRYPT_OK );
		}

	/* Read the payload data from the client/server */
	status = netStream->bufferedTransportReadFunction( stream, 
							httpDataInfo->buffer, httpDataInfo->bytesAvail,
							&httpDataInfo->bytesTransferred, 
							TRANSPORT_FLAG_NONE );
	if( cryptStatusError( status ) )
		return( status );
	if( httpDataInfo->bytesTransferred < httpDataInfo->bytesAvail )
		{
		/* We timed out before reading all of the data.  Usually this will 
		   be reported as a CRYPT_ERROR_TIMEOUT by the lower-level read
		   routines, however due to the multiple layers of I/O and special
		   case timeout handling when (for example) a cryptlib transport
		   session is layered over the network I/O layer, we perform an
		   explicit check here to make sure that we got everything */
		retExt( CRYPT_ERROR_TIMEOUT,
				( CRYPT_ERROR_TIMEOUT, NETSTREAM_ERRINFO, 
				  "HTTP read timed out before all data could be read, only "
				  "got %d of %d bytes", httpDataInfo->bytesTransferred, 
				  httpDataInfo->bytesAvail ) );
		}

	/* If it's a plain-text error message, return it to the caller */
	if( flags & HTTP_FLAG_TEXTMSG )
		{
		BYTE *byteBufPtr = httpDataInfo->buffer;

		/* Usually a body returned as plain text is an error message that
		   (for some reason) is sent as content rather than an HTTP error,
		   however in some unusual cases the content will be the requested
		   object marked as plain text.  We try and filter out genuine PKI
		   data erroneously marked as text by requiring that the request is 
		   over a minimum size (most error messages are quite short) and 
		   that the first bytes match what would be seen in a PKI object 
		   such as a cert or CRL */
		if( httpDataInfo->bytesAvail < 256 || ( byteBufPtr[ 0 ] != 0x30 ) || \
			!( byteBufPtr[ 1 ] & 0x80 ) || \
			( isAlpha( byteBufPtr[ 2 ] ) && isAlpha( byteBufPtr[ 3 ] ) && \
			  isAlpha( byteBufPtr[ 4 ] ) ) )
			{
			retExt( CRYPT_ERROR_READ,
					( CRYPT_ERROR_READ, NETSTREAM_ERRINFO, 
					  "HTTP server reported: '%s'",
					  sanitiseString( byteBufPtr, \
									  httpDataInfo->bufSize,
									  min( httpDataInfo->bytesTransferred, \
										   MAX_ERRMSG_SIZE - 32 ) ) ) );
			}
		}

	/* If we're reading chunked data, drain the input by processing the
	   trailer.  The reason why there can be extra header lines at the end
	   of the chunked data is because it's designed to be an indefinite-
	   length streamable format that doesn't require buffering the entire
	   message before emitting it.  Since some header information may not be
	   available until the entire message has been generated, the HTTP spec.
	   makes provisions for adding further header lines as a trailer.  In
	   theory we should check for the HTTP_FLAG_TRAILER flag before reading
	   trailer lines rather than just swallowing the last CRLF, however the
	   "Trailer:" header wasn't added until RFC 2616 (RFC 2068 didn't have
	   it) so we can't rely on its presence.  Normally we wouldn't have to
	   worry about trailer data, but if it's an HTTP 1.1 persistent
	   connection we then need to clear the way for the next lot of data */
	if( flags & HTTP_FLAG_CHUNKED )
		{
		status = readTrailerLines( stream, headerBuffer,
								   HTTP_LINEBUF_SIZE );
		if( cryptStatusError( status ) )
			return( status );
		}

	*length = maxLength;
	return( CRYPT_OK );
	}

STDC_NONNULL_ARG( ( 1 ) ) \
void setStreamLayerHTTP( INOUT NET_STREAM_INFO *netStream )
	{
	assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );

	/* Set the access method pointers */
	netStream->readFunction = readFunction;
	setStreamLayerHTTPwrite( netStream );

	/* The default HTTP operation type is POST, since in most cases it's 
	   being used as a substrate by a PKI protocol */
	netStream->nFlags |= STREAM_NFLAG_HTTPPOST;

	/* HTTP provides its own data-size and flow-control indicators so we
	   don't want the higher-level code to try and do this for us */
	netStream->nFlags |= STREAM_NFLAG_ENCAPS;
	}
#endif /* USE_HTTP */

⌨️ 快捷键说明

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