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 + -
显示快捷键?