http_parse.c

来自「cryptlib安全工具包」· C语言 代码 · 共 1,421 行 · 第 1/4 页

C
1,421
字号
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int retHeaderError( INOUT STREAM *stream, 
						   FORMAT_STRING const char *format, 
						   IN_BUFFER( strArgLen ) char *strArg, 
						   IN_LENGTH_SHORT const int strArgLen, 
						   const int lineNo )
	{
	NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( format, 4 ) );
	assert( isWritePtr( strArg, strArgLen ) );

	REQUIRES( strArgLen > 0 && strArgLen < MAX_INTLENGTH_SHORT );

	/* Format the error information.  We add two to the line number since 
	   it's zero-based and the header counts as an extra line */
	retExt( CRYPT_ERROR_BADDATA,
			( CRYPT_ERROR_BADDATA, NETSTREAM_ERRINFO, format,
			  sanitiseString( strArg, strArgLen, 
							  min( strArgLen, CRYPT_MAX_TEXTSIZE ) ),
			  lineNo + 2 ) );
	}

/* Send an HTTP error message.  This function is somewhat unusually placed
   with the general HTTP parsing functions because it's used by both read 
   and write code but needs access to the HTTP status decoding table, which 
   is part of the parsing code */

STDC_NONNULL_ARG( ( 1, 2 ) ) \
int sendHTTPError( INOUT STREAM *stream, 
				   IN_BUFFER( headerBufMaxLen ) char *headerBuffer, 
				   IN_LENGTH_SHORT_MIN( 256 ) const int headerBufMaxLen, 
				   IN_INT const int httpStatus )
	{
	STREAM headerStream;
	const char *statusString = "400";
	const char *errorString = "Bad Request";
	int errorStringLength = 11, length = DUMMY_INIT, i, status;

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

	REQUIRES( headerBufMaxLen >= 256 && \
			  headerBufMaxLen < MAX_INTLENGTH_SHORT );

	/* Find the HTTP error string that corresponds to the HTTP status
	   value */
	for( i = 0; httpStatusInfo[ i ].httpStatus > 0 && \
				httpStatusInfo[ i ].httpStatus != httpStatus && \
				i < FAILSAFE_ARRAYSIZE( httpStatusInfo, HTTP_STATUS_INFO ); 
		 i++ );
	ENSURES( i < FAILSAFE_ARRAYSIZE( httpStatusInfo, HTTP_STATUS_INFO ) );
	if( httpStatusInfo[ i ].httpStatus > 0 )
		{
		statusString = httpStatusInfo[ i ].httpStatusString;
		errorString = httpStatusInfo[ i ].httpErrorString;
		errorStringLength = httpStatusInfo[ i ].httpErrorStringLength;
		}

	/* Send the error message to the peer */
	sMemOpen( &headerStream, headerBuffer, headerBufMaxLen );
	swrite( &headerStream, isHTTP10( stream ) ? "HTTP/1.0 " : \
												"HTTP/1.1 ", 9 );
	swrite( &headerStream, statusString, HTTP_STATUSSTRING_LENGTH );
	sputc( &headerStream, ' ' );
	swrite( &headerStream, errorString, errorStringLength );
	status = swrite( &headerStream, "\r\n\r\n", 4 );
	if( cryptStatusOK( status ) )
		length = stell( &headerStream );
	sMemDisconnect( &headerStream );
	ENSURES( cryptStatusOK( status ) );
	return( sendHTTPData( stream, headerBuffer, length,
						  TRANSPORT_FLAG_FLUSH ) );
	}

/****************************************************************************
*																			*
*							URI Parsing Functions							*
*																			*
****************************************************************************/

/* Information needed to parse a URI sub-segment: The character that ends a
   segment and an optional alternative segment-end character, and the
   minimum and maximum permitted segment size.  The alternative segment-end
   character is used for strings like:

	type-info [; more-info]

   where optional additional information may follow the value that we're
   interested in, separated by a delimiter.  The formatting of the parse
   info is one of:

	endChar	altEndChar	Matches
	-------	----------	------------------
		x		\0		....x.... (Case 1)
		x		y		....x....
						....y....
		\0		y		....	  (Case 2)
						....y.... */

typedef struct {
	const char segmentEndChar, altSegmentEndChar;
	const int segmentMinLength, segmentMaxLength;
	} URI_PARSE_INFO;

/* Get the length of a sub-segment of a URI */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
static int getUriSegmentLength( OUT_BUFFER( dataMaxLength, *dataLength ) \
									const char *data, 
								IN_LENGTH_SHORT const int dataMaxLength, 
								OUT_LENGTH_SHORT_Z int *dataLength, 
								const URI_PARSE_INFO *uriParseInfo,
								BOOLEAN *altDelimiterFound )
	{
	const int maxLength = min( dataMaxLength, uriParseInfo->segmentMaxLength );
	int i;

	assert( isReadPtr( data, dataMaxLength ) );
	assert( isWritePtr( dataLength, sizeof( int ) ) );
	assert( isReadPtr( uriParseInfo, sizeof( URI_PARSE_INFO  ) ) );
	assert( ( uriParseInfo->altSegmentEndChar == '\0' && \
			  altDelimiterFound == NULL ) || \
			( uriParseInfo->altSegmentEndChar > '\0' && \
			  isWritePtr( altDelimiterFound, sizeof( BOOLEAN ) ) ) );

	REQUIRES( maxLength > 0 && maxLength < MAX_INTLENGTH_SHORT );
	REQUIRES( uriParseInfo->segmentMinLength >= 0 && \
			  uriParseInfo->segmentMinLength < \
					uriParseInfo->segmentMaxLength && \
			  uriParseInfo->segmentMaxLength <= 1024 );
	REQUIRES( ( uriParseInfo->altSegmentEndChar == '\0' && \
				altDelimiterFound == NULL ) || \
			  ( uriParseInfo->altSegmentEndChar > '\0' && \
				altDelimiterFound != NULL ) );

	/* Clear return value */
	*dataLength = 0;
	if( altDelimiterFound != NULL )
		*altDelimiterFound = FALSE;

	/* Parse the current query sub-segment */
	for( i = 0; i < maxLength; i++ )
		{
		if( data[ i ] == uriParseInfo->segmentEndChar )
			break;
		if( uriParseInfo->altSegmentEndChar > '\0' && \
			data[ i ] == uriParseInfo->altSegmentEndChar )
			{
			*altDelimiterFound = TRUE;
			break;
			}
		}

	/* If there's an end-char specified (Case 1) and we didn't find it (or 
	   the alternative end-char if there is one), it's an error.  If there's
	   no end-char specified (Case 2), the end of the sub-segment is at the
	   end of the data */
	if( uriParseInfo->segmentEndChar != '\0' && i >= dataMaxLength )
		return( CRYPT_ERROR_BADDATA );

	/* Make sure that we both got enough data and that we didn't run out of
	   data */
	if( i < uriParseInfo->segmentMinLength || \
		i >= uriParseInfo->segmentMaxLength )
		return( CRYPT_ERROR_BADDATA );

	*dataLength = i;
	return( CRYPT_OK );
	}

/* Parse a URI of the form "* '?' attribute '=' value [ '&' ... ] ' ' ",
   returning the parsed form to the caller (there's always a space at the
   end because it's followed by the HTTP ID string).  This function needs to 
   return two length values since it decodes the URI string according to RFC 
   1866, which means that its length can change.  So as its standard return 
   value it returns the number of chars consumed, but it also returns the 
   new length of the input as a by-reference parameter */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
int parseUriInfo( OUT_BUFFER( dataInLength, *dataOutLength ) char *data, 
				  IN_LENGTH_SHORT const int dataInLength, 
				  OUT_LENGTH_SHORT_Z int *dataOutLength, 
				  INOUT HTTP_URI_INFO *uriInfo )
	{
	static const URI_PARSE_INFO locationParseInfo = \
			{ '?', '\0', 1, CRYPT_MAX_TEXTSIZE };
	static const URI_PARSE_INFO attributeParseInfo = \
			{ '=', '\0', 3, CRYPT_MAX_TEXTSIZE };
	static const URI_PARSE_INFO valueParseInfo = \
			{ ' ', '&', 3, CRYPT_MAX_TEXTSIZE };
	static const URI_PARSE_INFO extraParseInfo = \
			{ ' ', '\0', 1, CRYPT_MAX_TEXTSIZE };
	BOOLEAN altDelimiterFound;
	const char *bufPtr = data;
	int length = dataInLength, segmentLength, parsedLength, i, status;

	assert( isWritePtr( data, dataInLength ) );
	assert( isWritePtr( dataOutLength, sizeof( int ) ) );
	assert( isWritePtr( uriInfo, sizeof( HTTP_URI_INFO ) ) );

	REQUIRES( dataInLength > 0 && dataInLength < MAX_INTLENGTH_SHORT );

	/* Clear return values */
	memset( uriInfo, 0, sizeof( HTTP_URI_INFO ) );
	*dataOutLength = 0;

	/* Decode the URI text.  Since there can be multiple nested levels of
	   encoding, we keep iteratively decoding in-place until either 
	   decodeRFC1866() cries Uncle or we hit the sanity-check limit */
	for( i = 0; i < FAILSAFE_ITERATIONS_SMALL; i++ )
		{
		status = decodeRFC1866( data, length );
		if( cryptStatusError( status ) )
			{
			if( status == OK_SPECIAL )
				/* There's been no further change in the data, exit */
				break;
			return( CRYPT_ERROR_BADDATA );
			}
		length = status;	/* Record the new length of the decoded data */
		}
	if( i >= FAILSAFE_ITERATIONS_SMALL )
		{
		/* Sanity-check limit exceeded.  This could be either a data error
		   or an internal error, since we can't automatically tell which it 
		   is we report it as a data error */
		return( CRYPT_ERROR_BADDATA );
		}
	*dataOutLength = length;

	/* We need to get at least 'x?xxx=xxx' */
	if( length < 9 )
		return( CRYPT_ERROR_BADDATA );

	/* Parse a URI of the form "* '?' attribute '=' value [ '&' ... ] ' ' ".
	   The URI is followed by the HTTP ID, so we know that it always has to
	   end on a space; running out of input is an error */
	status = getUriSegmentLength( bufPtr, length, &segmentLength,
								  &locationParseInfo, NULL );
	if( cryptStatusError( status ) )
		return( status );
	memcpy( uriInfo->location, bufPtr, segmentLength );
	uriInfo->locationLen = segmentLength;
	bufPtr += segmentLength + 1;	/* Skip delimiter */
	length -= segmentLength + 1;
	parsedLength = segmentLength + 1;
	status = getUriSegmentLength( bufPtr, length, &segmentLength,
								  &attributeParseInfo, NULL );
	if( cryptStatusError( status ) )
		return( status );
	memcpy( uriInfo->attribute, bufPtr, segmentLength );
	uriInfo->attributeLen = segmentLength;
	bufPtr += segmentLength + 1;	/* Skip delimiter */
	length -= segmentLength + 1;
	parsedLength += segmentLength + 1;
	status = getUriSegmentLength( bufPtr, length, &segmentLength,
								  &valueParseInfo, &altDelimiterFound );
	if( cryptStatusError( status ) )
		return( status );
	memcpy( uriInfo->value, bufPtr, segmentLength );
	uriInfo->valueLen = segmentLength;
	bufPtr += segmentLength + 1;	/* Skip delimiter */
	length -= segmentLength + 1;
	parsedLength += segmentLength + 1;
	if( altDelimiterFound )
		{
		status = getUriSegmentLength( bufPtr, length, &segmentLength,
									  &extraParseInfo, NULL );
		if( cryptStatusError( status ) )
			return( status );
		memcpy( uriInfo->extraData, bufPtr, segmentLength );
		uriInfo->extraDataLen = segmentLength;
		parsedLength += segmentLength + 1;
		}

	return( parsedLength );
	}

/* Check an "HTTP 1.x" ID string.  No PKI client should be sending us a 0.9
   ID, so we only allow 1.x */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
int checkHTTPID( IN_BUFFER( dataLength ) const char *data, 
				 IN_LENGTH_SHORT const int dataLength, 
				 INOUT STREAM *stream )
	{
	NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;

	assert( isReadPtr( data, dataLength ) );
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );

	if( dataLength < 8 || strCompare( data, "HTTP/1.", 7 ) )
		return( CRYPT_ERROR_BADDATA );
	if( data[ 7 ] == '0' )
		netStream->nFlags |= STREAM_NFLAG_HTTP10;
	else
		{
		if( data[ 7 ] != '1' )
			return( CRYPT_ERROR_BADDATA );
		}

	return( 8 );
	}

/****************************************************************************
*																			*
*							HTTP Header Processing							*
*																			*
****************************************************************************/

/* Read an HTTP status code.  Some status values are warnings only and
   don't return an error status */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
static int readHTTPStatus( IN_BUFFER( dataLength ) const char *data, 
						   IN_LENGTH_SHORT const int dataLength,
						   OUT_OPT_RANGE( 0, 999 ) int *httpStatus, 
						   INOUT ERROR_INFO *errorInfo )
	{
	const HTTP_STATUS_INFO *httpStatusPtr;
	const BOOLEAN isResponseStatus = ( httpStatus != NULL ) ? TRUE : FALSE;
	char thirdChar;
	int value, remainderLength, offset, i, status;

	assert( isReadPtr( data, dataLength ) );
	assert( httpStatus == NULL || \
			isWritePtr( httpStatus, sizeof( int ) ) );

	REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );
	REQUIRES( errorInfo != NULL );

	/* Clear return value */
	if( httpStatus != NULL )
		*httpStatus = 999;

	/* Check that the numeric value is in order, being exactly three 
	   characters followed by a space */
	if( dataLength < 3 || strSkipNonWhitespace( data, dataLength ) != 3 )
		{
		retExt( CRYPT_ERROR_BADDATA, 
				( CRYPT_ERROR_BADDATA, errorInfo, 
				  "Invalid/missing HTTP %sstatus code", 
				  isResponseStatus ? "response " : "" ) );
		}

	/* Process the three-digit numeric status code */
	status = strGetNumeric( data, 3, &value, 1, 999 );
	if( cryptStatusError( status ) )
		{
		retExt( CRYPT_ERROR_BADDATA, 
				( CRYPT_ERROR_BADDATA, errorInfo, 
				  "Invalid HTTP %sstatus code", 

⌨️ 快捷键说明

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