http_parse.c
来自「cryptlib安全工具包」· C语言 代码 · 共 1,421 行 · 第 1/4 页
C
1,421 行
/****************************************************************************
* *
* cryptlib HTTP Parsing 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
/* The various HTTP header types that we can process */
typedef enum { HTTP_HEADER_NONE, HTTP_HEADER_HOST, HTTP_HEADER_CONTENT_LENGTH,
HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_TRANSFER_ENCODING,
HTTP_HEADER_CONTENT_ENCODING,
HTTP_HEADER_CONTENT_TRANSFER_ENCODING, HTTP_HEADER_TRAILER,
HTTP_HEADER_CONNECTION, HTTP_HEADER_WARNING,
HTTP_HEADER_LOCATION, HTTP_HEADER_EXPECT, HTTP_HEADER_LAST
} HTTP_HEADER_TYPE;
/* HTTP header parsing information. Note that the first letter of the
header string must be uppercase for the case-insensitive quick match */
#define HTTP_STATUSSTRING_LENGTH 3
typedef struct {
BUFFER_FIXED( headerStringLen ) \
const char FAR_BSS *headerString;/* Header string */
const int headerStringLen; /* Length of header string */
const HTTP_HEADER_TYPE headerType; /* Type corresponding to header string */
} HTTP_HEADER_PARSE_INFO;
static const HTTP_HEADER_PARSE_INFO FAR_BSS httpHeaderParseInfo[] = {
{ "Host:", 5, HTTP_HEADER_HOST },
{ "Content-Length:", 15, HTTP_HEADER_CONTENT_LENGTH },
{ "Content-Type:", 13, HTTP_HEADER_CONTENT_TYPE },
{ "Transfer-Encoding:", 18, HTTP_HEADER_TRANSFER_ENCODING },
{ "Content-Encoding:", 17, HTTP_HEADER_CONTENT_ENCODING },
{ "Content-Transfer-Encoding:", 26, HTTP_HEADER_CONTENT_TRANSFER_ENCODING },
{ "Trailer:", 8, HTTP_HEADER_TRAILER },
{ "Connection:", 11, HTTP_HEADER_CONNECTION },
{ "NnCoection:", 11, HTTP_HEADER_CONNECTION },
{ "Cneonction:", 11, HTTP_HEADER_CONNECTION },
/* The bizarre spellings are for buggy NetApp NetCache servers,
which unfortunately are widespread enough that we need to provide
special-case handling for them. For the second mis-spelling we
have to capitalise the first letter for our use since we compare
the uppercase form for a quick match */
{ "Warning:", 8, HTTP_HEADER_WARNING },
{ "Location:", 9, HTTP_HEADER_LOCATION },
{ "Expect:", 7, HTTP_HEADER_EXPECT },
{ NULL, 0, HTTP_HEADER_NONE }
};
/* HTTP error/warning messages. The mapped status for 30x redirects is
somewhat special-case, see the comment in readResponseHeader() for
details. This table also contains known non-HTTP codes in the
expectation that, when used as a general-purpose substrate, it'll be
pressed into use in all sorts of situations */
typedef struct {
const int httpStatus; /* Numeric status value */
BUFFER_FIXED( HTTP_STATUSSTRING_LENGTH ) \
const char FAR_BSS *httpStatusString; /* String status value */
BUFFER_FIXED( httpErrorStringLength ) \
const char FAR_BSS *httpErrorString; /* Text description of status */
const int httpErrorStringLength;
const int status; /* Equivalent cryptlib status */
} HTTP_STATUS_INFO;
static const HTTP_STATUS_INFO FAR_BSS httpStatusInfo[] = {
{ 100, "100", "Continue", 8, OK_SPECIAL },
{ 101, "101", "Switching Protocols", 19, CRYPT_ERROR_READ },
{ 110, "110", "Warning: Response is stale", 26, CRYPT_OK },
{ 111, "111", "Warning: Revalidation failed", 28, CRYPT_OK },
{ 112, "112", "Warning: Disconnected operation", 31, CRYPT_OK },
{ 113, "113", "Warning: Heuristic expiration", 29, CRYPT_OK },
{ 199, "199", "Warning: Miscellaneous warning", 30, CRYPT_OK },
{ 200, "200", "OK", 2, CRYPT_OK },
{ 201, "201", "Created", 7, CRYPT_ERROR_READ },
{ 202, "202", "Accepted", 8, CRYPT_ERROR_READ },
{ 203, "203", "Non-Authoritative Information", 29, CRYPT_OK },
{ 204, "204", "No Content", 10, CRYPT_ERROR_READ },
{ 205, "205", "Reset Content", 13, CRYPT_ERROR_READ },
{ 206, "206", "Partial Content", 15, CRYPT_ERROR_READ },
{ 214, "214", "Warning: Transformation applied", 31, CRYPT_OK },
{ 250, "250", "RTSP: Low on Storage Space", 26, CRYPT_OK },
{ 299, "299", "Warning: Miscellaneous persistent warning", 41, CRYPT_OK },
{ 300, "300", "Multiple Choices", 16, CRYPT_ERROR_READ },
{ 301, "301", "Moved Permanently", 17, OK_SPECIAL },
{ 302, "302", "Moved Temporarily/Found", 23, OK_SPECIAL },
{ 303, "303", "See Other", 9, CRYPT_ERROR_READ },
{ 304, "304", "Not Modified", 12, CRYPT_ERROR_READ },
{ 305, "305", "Use Proxy", 9, CRYPT_ERROR_READ },
{ 306, "306", "Unused/obsolete", 15, CRYPT_ERROR_READ },
{ 307, "307", "Temporary Redirect", 18, OK_SPECIAL },
{ 400, "400", "Bad Request", 11, CRYPT_ERROR_READ },
{ 401, "401", "Unauthorized", 12, CRYPT_ERROR_PERMISSION },
{ 402, "402", "Payment Required", 16, CRYPT_ERROR_READ },
{ 403, "403", "Forbidden", 9, CRYPT_ERROR_PERMISSION },
{ 404, "404", "Not Found", 9, CRYPT_ERROR_NOTFOUND },
{ 405, "405", "Method Not Allowed", 18, CRYPT_ERROR_NOTAVAIL },
{ 406, "406", "Not Acceptable", 14, CRYPT_ERROR_PERMISSION },
{ 407, "407", "Proxy Authentication Required", 29, CRYPT_ERROR_PERMISSION },
{ 408, "408", "Request Time-out", 16, CRYPT_ERROR_READ },
{ 409, "409", "Conflict", 8, CRYPT_ERROR_READ },
{ 410, "410", "Gone", 4, CRYPT_ERROR_NOTFOUND },
{ 411, "411", "Length Required", 15, CRYPT_ERROR_READ },
{ 412, "412", "Precondition Failed", 19, CRYPT_ERROR_READ },
{ 413, "413", "Request Entity too Large", 24, CRYPT_ERROR_OVERFLOW },
{ 414, "414", "Request-URI too Large", 21, CRYPT_ERROR_OVERFLOW },
{ 415, "415", "Unsupported Media Type", 22, CRYPT_ERROR_READ },
{ 416, "416", "Requested range not satisfiable", 31, CRYPT_ERROR_READ },
{ 417, "417", "Expectation Failed", 18, CRYPT_ERROR_READ },
{ 426, "426", "Upgrade Required", 16, CRYPT_ERROR_READ },
{ 451, "451", "RTSP: Parameter not Understood", 30, CRYPT_ERROR_BADDATA },
{ 452, "452", "RTSP: Conference not Found", 26, CRYPT_ERROR_NOTFOUND },
{ 453, "453", "RTSP: Not enough Bandwidth", 26, CRYPT_ERROR_NOTAVAIL },
{ 454, "454", "RTSP: Session not Found", 23, CRYPT_ERROR_NOTFOUND },
{ 455, "455", "RTSP: Method not Valid in this State", 36, CRYPT_ERROR_NOTAVAIL },
{ 456, "456", "RTSP: Header Field not Valid for Resource", 41, CRYPT_ERROR_NOTAVAIL },
{ 457, "457", "RTSP: Invalid Range", 19, CRYPT_ERROR_READ },
{ 458, "458", "RTSP: Parameter is Read-Only", 28, CRYPT_ERROR_PERMISSION },
{ 459, "459", "RTSP: Aggregate Operation not Allowed", 37, CRYPT_ERROR_PERMISSION },
{ 460, "460", "RTSP: Only Aggregate Operation Allowed", 38, CRYPT_ERROR_PERMISSION },
{ 461, "461", "RTSP: Unsupported Transport", 27, CRYPT_ERROR_NOTAVAIL },
{ 462, "462", "RTSP: Destination Unreachable", 29, CRYPT_ERROR_OPEN },
{ 500, "500", "Internal Server Error", 21, CRYPT_ERROR_READ },
{ 501, "501", "Not Implemented", 15, CRYPT_ERROR_NOTAVAIL },
{ 502, "502", "Bad Gateway", 11, CRYPT_ERROR_READ },
{ 503, "503", "Service Unavailable", 19, CRYPT_ERROR_NOTAVAIL },
{ 504, "504", "Gateway Time-out", 16, CRYPT_ERROR_TIMEOUT },
{ 505, "505", "HTTP Version not supported", 26, CRYPT_ERROR_READ },
{ 510, "510", "HTTP-Ext: Not Extended", 22, CRYPT_ERROR_READ },
{ 551, "551", "RTSP: Option not supported", 26, CRYPT_ERROR_READ },
{ 0, NULL, "Unrecognised HTTP status condition", 34, CRYPT_ERROR_READ },
{ 0, NULL, "Unrecognised HTTP status condition", 34, CRYPT_ERROR_READ }
};
/****************************************************************************
* *
* 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 */
CALLBACK_FUNCTION 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( streamPtr, sizeof( STREAM ) ) );
status = netStream->bufferedTransportReadFunction( stream, &ch, 1,
&length,
TRANSPORT_FLAG_NONE );
return( cryptStatusError( status ) ? status : ch );
}
/* Decode a hex nibble */
CHECK_RETVAL \
static int getNibble( IN_CHAR const char srcCh )
{
int ch;
ch = toLower( srcCh );
if( !isXDigit( ch ) )
return( CRYPT_ERROR_BADDATA );
return( ( ch <= '9' ) ? ch - '0' : ch - ( 'a' - 10 ) );
}
/* Decode an escaped character */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int getEncodedChar( IN_BUFFER( bufSize ) const char *buffer,
IN_LENGTH_SHORT const int bufSize )
{
int chLo, chHi, ch;
assert( isReadPtr( buffer, bufSize ) );
REQUIRES( bufSize > 0 && bufSize < MAX_INTLENGTH_SHORT );
/* Make sure that there's enough data left to decode the character */
if( bufSize < 2 )
return( CRYPT_ERROR_BADDATA );
/* Recreate the original from the hex value */
chHi = getNibble( buffer[ 0 ] );
chLo = getNibble( buffer[ 1 ] );
if( cryptStatusError( chHi ) || cryptStatusError( chLo ) )
return( CRYPT_ERROR_BADDATA );
ch = ( chHi << 4 ) | chLo;
/* It's a special-case/control character of some kind, report it as an
error. This gets rid of things like nulls (treated as string
terminators by some functions) and CR/LF line terminators, which can
be embedded into strings to turn a single line of supplied text into
multi-line responses containing user-controlled type : value pairs
(in other words they allow user data to be injected into the control
channel) */
if( ch <= 0 || ch > 0x7F || !isPrint( ch ) )
return( CRYPT_ERROR_BADDATA );
return( ch );
}
/* Decode a string as per RFC 1866 */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int decodeRFC1866( IN_BUFFER( bufSize ) char *buffer,
IN_LENGTH_SHORT const int bufSize )
{
int srcIndex = 0, destIndex = 0;
assert( isWritePtr( buffer, bufSize ) );
REQUIRES( bufSize > 0 && bufSize < MAX_INTLENGTH_SHORT );
while( srcIndex < bufSize )
{
int ch = buffer[ srcIndex++ ];
/* If it's an escaped character, decode it. If it's not escaped we
can copy it straight over, the input has already been sanitised
when it was read so there's no need to perform another check
here */
if( ch == '%' )
{
ch = getEncodedChar( buffer + srcIndex, bufSize - srcIndex );
if( cryptStatusError( ch ) )
return( ch );
srcIndex += 2;
}
buffer[ destIndex++ ] = ch;
}
/* If we've processed an escape sequence (causing the data to change
size), tell the caller the new length, otherwise tell them that
nothing's changed */
return( ( destIndex < srcIndex ) ? destIndex : OK_SPECIAL );
}
/* Convert a hex ASCII string used with chunked encoding into a numeric
value */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int getChunkLength( IN_BUFFER( dataLength ) const char *data,
IN_LENGTH_SHORT const int dataLength )
{
int i, chunkLength = 0, length = dataLength;
assert( isReadPtr( data, dataLength ) );
REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT );
/* Chunk size information can have extensions tacked onto it following a
';', strip these before we start */
for( i = 0; i < length; i++ )
{
if( data[ i ] == ';' )
{
/* Move back to the end of the string that precedes the ';' */
while( i > 0 && data[ i - 1 ] == ' ' )
i--;
length = i; /* Adjust length and force loop exit */
}
}
/* The other side shouldn't be sending us more than 64K of data, given
that what we're expecting is a short PKI message */
if( length < 1 || length > 4 )
return( CRYPT_ERROR_BADDATA );
/* Walk down the string converting hex characters into their numeric
values. Unfortunately since it's hex we can't use strGetNumeric()
to read the value but have to hand-assemble it ourselves */
for( i = 0; i < length; i++ )
{
const int ch = getNibble( data[ i ] );
if( cryptStatusError( ch ) )
return( CRYPT_ERROR_BADDATA );
chunkLength = ( chunkLength << 4 ) | ch;
}
if( chunkLength < 0 || chunkLength >= MAX_INTLENGTH )
return( CRYPT_ERROR_BADDATA );
return( chunkLength );
}
/****************************************************************************
* *
* Error Handling Functions *
* *
****************************************************************************/
/* Exit with extended error information after a readTextLine() call */
STDC_NONNULL_ARG( ( 1, 4 ) ) \
int retTextLineError( INOUT STREAM *stream, IN_ERROR const int status,
const BOOLEAN isTextLineError,
FORMAT_STRING const char *format,
const int value )
{
NET_STREAM_INFO *netStream = ( NET_STREAM_INFO * ) stream->netStreamInfo;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( cryptStatusError( status ) );
assert( isReadPtr( format, 4 ) );
/* If the extended error information came up from a lower level than
readCharFunction(), pass it on up to the caller */
if( !isTextLineError )
return( status );
/* Extend the readTextLine()-level error information with higher-level
detail. This allows us to provide a more useful error report
("Problem with line x") than just the rather low-level view provided
by readTextLine() ("Invalid character 0x8F at position 12"). The
argument handling is:
printf( format, value );
printf( stream->errorInfo->errorString );
so that 'format' has the form 'High-level error %d: ", to which the
low-level string is then appended */
retExtErr( status,
( status, NETSTREAM_ERRINFO, NETSTREAM_ERRINFO,
format, value ) );
}
/* Exit with extended error information relating to header-line parsing */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?