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

📄 http_rd.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************
*																			*
*						  cryptlib HTTP Read Routines						*
*						Copyright Peter Gutmann 1998-2007					*
*																			*
****************************************************************************/

#include <ctype.h>
#include <stdio.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "http.h"
  #include "misc_rw.h"
#else
  #include "crypt.h"
  #include "io/http.h"
  #include "misc/misc_rw.h"
#endif /* Compiler-specific includes */

#ifdef USE_HTTP

/* HTTP request line parsing information */

typedef struct {
	BUFFER_FIXED( reqNameLen ) \
	const char *reqName;		/* Request name */
	int reqNameLen;				/* Length of request name */
	STREAM_HTTPREQTYPE_TYPE reqType;	/* Request type */
	int reqTypeFlag;			/* Stream flag for this request type */
	} HTTP_REQUEST_INFO;

static const HTTP_REQUEST_INFO FAR_BSS httpReqInfo[] = {
	{ "GET", 3, STREAM_HTTPREQTYPE_GET, STREAM_NFLAG_HTTPGET },
	{ "POST", 4, STREAM_HTTPREQTYPE_POST, STREAM_NFLAG_HTTPPOST },
	{ NULL, 0, 0 }, { NULL, 0, 0 }
	};

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* Callback function used by readTextLine() to read characters from a
   stream.  When reading text data over a network we don't know how much
   more data is to come so we have to read a byte at a time looking for an
   EOL.  In addition we can't use the simple optimisation of reading two
   bytes at a time because some servers only send a LF even though the spec
   requires a CRLF.  This is horribly inefficient but is pretty much
   eliminated through the use of opportunistic read-ahead buffering */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int readCharFunction( INOUT TYPECAST( STREAM * ) void *streamPtr )
	{
	STREAM *stream = streamPtr;
	NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
	BYTE ch;
	int length, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	status = netStream->bufferedTransportReadFunction( stream, &ch, 1, 
													   &length,
													   TRANSPORT_FLAG_NONE );
	return( cryptStatusError( status ) ? status : ch );
	}

/****************************************************************************
*																			*
*								Read Request Header							*
*																			*
****************************************************************************/

/* Read an HTTP request header */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
static int readRequestHeader( INOUT STREAM *stream, 
							  OUT_BUFFER_FIXED( lineBufSize ) char *lineBuffer, 
							  IN_LENGTH_SHORT_MIN( 256 ) const int lineBufSize, 
							  INOUT HTTP_DATA_INFO *httpDataInfo, 
							  OUT_FLAGS_Z( HTTP ) int *flags )
	{
	NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
	HTTP_HEADER_INFO headerInfo;
	HTTP_URI_INFO *uriInfo = httpDataInfo->reqInfo;
	STREAM_HTTPREQTYPE_TYPE reqType = STREAM_HTTPREQTYPE_NONE;
	BOOLEAN isTextDataError;
	char *bufPtr;
	int length, offset, reqNameLen = DUMMY_INIT, i, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( lineBuffer, lineBufSize ) );
	assert( isWritePtr( httpDataInfo, sizeof( HTTP_DATA_INFO ) ) );
	assert( isWritePtr( flags, sizeof( int ) ) );

	REQUIRES( netStream->nFlags & STREAM_NFLAG_ISSERVER );
	REQUIRES( lineBufSize >= 256 && lineBufSize < MAX_INTLENGTH_SHORT );

	/* Clear return value */
	*flags = HTTP_FLAG_NONE;

	/* Read the header and check for "POST/GET x HTTP/1.x".  In theory this
	   could be a bit risky because the original CERN server required an
	   extra (spurious) CRLF after a POST, so that various early clients sent
	   an extra CRLF that isn't included in the Content-Length header and
	   ends up preceding the start of the next load of data.  We don't check
	   for this because it only applies to very old pure-HTTP (rather than
	   HTTP-as-a-transport-layer) clients, which are unlikely to be hitting a
	   PKI responder */
	status = readTextLine( readCharFunction, stream, lineBuffer, 
						   lineBufSize, &length, &isTextDataError );
	if( cryptStatusError( status ) )
		{
		/* If it's an HTTP-level error (e.g. line too long), send back an
		   HTTP-level error response */
		if( status != CRYPT_ERROR_COMPLETE )
			sendHTTPError( stream, lineBuffer, lineBufSize,
						   ( status == CRYPT_ERROR_OVERFLOW ) ? 414 : 400 );

		return( retTextLineError( stream, status, isTextDataError, 
								  "Invalid HTTP request header line 1: ", 
								  0 ) );
		}
	for( i = 0; httpReqInfo[ i ].reqName != NULL && \
				i < FAILSAFE_ARRAYSIZE( httpReqInfo, HTTP_REQUEST_INFO );
		 i++ )
		{
		const HTTP_REQUEST_INFO *reqInfoPtr = &httpReqInfo[ i ];

		if( ( reqInfoPtr->reqTypeFlag & netStream->nFlags ) && \
			length >= reqInfoPtr->reqNameLen && \
			!strCompare( lineBuffer, reqInfoPtr->reqName, \
						 reqInfoPtr->reqNameLen ) )
			{
			reqType = reqInfoPtr->reqType;
			reqNameLen = reqInfoPtr->reqNameLen;
			break;
			}
		}
	ENSURES( i < FAILSAFE_ARRAYSIZE( httpReqInfo, HTTP_REQUEST_INFO ) );
	if( reqType == STREAM_HTTPREQTYPE_NONE )
		{
		char reqNameBuffer[ 16 + 8 ];

		/* Return the extended error information */
		if( ( offset = strSkipNonWhitespace( lineBuffer, length ) ) > 0 )
			length = offset;
		memcpy( reqNameBuffer, lineBuffer, min( 16, length ) );
		sendHTTPError( stream, lineBuffer, lineBufSize, 501 );
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO, 
				  "Invalid HTTP request '%s'",
				  sanitiseString( reqNameBuffer, 16, length ) ) );
		}
	bufPtr = lineBuffer + reqNameLen;
	length -= reqNameLen;

	/* Process the ' '* * ' '* and check for the HTTP ID */
	if( length <= 0 || ( offset = strSkipWhitespace( bufPtr, length ) ) < 0 )
		{
		sendHTTPError( stream, lineBuffer, lineBufSize, 400 );
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO, 
				  "Missing HTTP request URI" ) );
		}
	bufPtr += offset;
	length -= offset;
	if( reqType == STREAM_HTTPREQTYPE_GET )
		{
		/* Safety check, make sure that we can handle the HTTP GET */
		REQUIRES( uriInfo != NULL );

		/* If it's an indempotent read the client is sending a GET rather
		   than submitting a POST, process the request details.  This
		   performs in-place decoding of (possibly encoded) data, so it
		   returns two length values, the new length after the in-place
		   decoding has occurred, and the offset of the next character of
		   data as usual */
		offset = parseUriInfo( bufPtr, length, &length, uriInfo );
		}
	else
		{
		/* For non-idempotent queries we don't care what the location is
		   since it's not relevant for anything, so we just skip the URI.
		   This also avoids complications with absolute vs. relative URLs,
		   character encoding/escape sequences, and so on */
		offset = strSkipNonWhitespace( bufPtr, length );
		}
	if( cryptStatusError( offset ) )
		{
		sendHTTPError( stream, lineBuffer, lineBufSize, 400 );
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO, 
				  "Invalid HTTP GET request URI" ) );
		}
	bufPtr += offset;
	length -= offset;
	if( length <= 0 || ( offset = strSkipWhitespace( bufPtr, length ) ) < 0 )
		{
		sendHTTPError( stream, lineBuffer, lineBufSize, 400 );
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO, 
				  "Missing HTTP request ID/version" ) );
		}
	bufPtr += offset;
	length -= offset;
	if( length <= 0 || \
		cryptStatusError( checkHTTPID( bufPtr, length, stream ) ) )
		{
		sendHTTPError( stream, lineBuffer, lineBufSize, 505 );
		retExt( CRYPT_ERROR_BADDATA,
				( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO, 
				  "Invalid HTTP request ID/version" ) );
		}

	/* Process the remaining header lines.  ~32 bytes is the minimum-size
	   object that can be returned from any HTTP-based message which is
	   exchanged by cryptlib, this being a TSP request */
	initHeaderInfo( &headerInfo, 32, httpDataInfo->bufSize, *flags );
	if( reqType == STREAM_HTTPREQTYPE_GET )
		{
		/* It's an HTTP get, make sure that we don't try and read a body */
		headerInfo.flags |= HTTP_FLAG_GET;
		}
	status = readHeaderLines( stream, lineBuffer, lineBufSize,
							  &headerInfo );
	if( cryptStatusError( status ) )
		{
		/* We always (try and) send an HTTP error response once we get to
		   this stage since chances are that it'll be a problem with an
		   HTTP header rather than a low-level network read problem */
		sendHTTPError( stream, lineBuffer, lineBufSize,
					   headerInfo.httpStatus );
		return( status );
		}

	/* Copy any status info back to the caller */
	httpDataInfo->reqType = reqType;
	if( reqType != STREAM_HTTPREQTYPE_GET )
		httpDataInfo->bytesAvail = headerInfo.contentLength;
	*flags = headerInfo.flags;

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*								Read Response Header						*
*																			*
****************************************************************************/

/* Read an HTTP response header */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
static int readResponseHeader( INOUT STREAM *stream, 
							   OUT_BUFFER_FIXED( lineBufSize ) char *lineBuffer, 
							   IN_LENGTH_SHORT_MIN( 256 ) const int lineBufSize, 
							   INOUT HTTP_DATA_INFO *httpDataInfo, 
							   OUT_FLAGS_Z( HTTP ) int *flags )
	{
	NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
	int repeatCount, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( lineBuffer, lineBufSize ) );
	assert( isWritePtr( httpDataInfo, sizeof( HTTP_DATA_INFO ) ) );
	assert( isWritePtr( flags, sizeof( int ) ) );

	REQUIRES( lineBufSize >= 256 && lineBufSize < MAX_INTLENGTH_SHORT );

	/* Clear return value */
	*flags = HTTP_FLAG_NONE;

	/* If it's a stateless HTTP read, we need to first send the initiating
	   HTTP GET request before we can read anything back */
	if( netStream->nFlags & STREAM_NFLAG_HTTPGET )
		{
		status = writeRequestHeader( stream, httpDataInfo->reqInfo, 
									 NULL, 0, 0 );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Read the returned response header from the server, taking various
	   special-case conditions into account.  In theory we could also handle
	   the 503 "Retry-After" status, but there's no sensible reason why
	   anyone should send us this, and even if they do it'll screw up a lot
	   of the PKI protocols, which have timeliness constraints built in */
	for( repeatCount = 0; repeatCount < FAILSAFE_ITERATIONS_SMALL; \
		 repeatCount++ )
		{
		HTTP_HEADER_INFO headerInfo;
		BOOLEAN needsSpecialHandling = FALSE, isSoftError = FALSE;
		int httpStatus;

⌨️ 快捷键说明

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