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

📄 http.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
/****************************************************************************
*																			*
*						cryptlib HTTP Interface Routines					*
*						Copyright Peter Gutmann 1998-2005					*
*																			*
****************************************************************************/

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "stream.h"
#elif defined( INC_CHILD )
  #include "../crypt.h"
  #include "stream.h"
#else
  #include "crypt.h"
  #include "io/stream.h"
#endif /* Compiler-specific includes */

#ifdef USE_HTTP

/* The maximum number of header lines that we'll read before giving up */

#define MAX_HEADER_LINES	25

/* The maximum number of retries for redirections (and, by extension,
   anything else that can loop), as per RFC 2616 */

#define MAX_RETRY_COUNT		5

/* The size of the HTTP text-line buffer when we're using a dedicated buffer 
   to read header lines, rather than the main stream buffer.  Anything more 
   than this is dropped */

#define HTTP_LINEBUF_SIZE	1024

/* A macro to determine whether we're talking HTTP 1.0 or 1.1 */

#define isHTTP10( stream ) \
		( ( stream )->flags & STREAM_NFLAG_HTTP10 )

/* HTTP state information passed around the various read/write functions */

#define HTTP_FLAG_NONE		0x00	/* No HTTP info */
#define HTTP_FLAG_CHUNKED	0x01	/* Message used chunked encoding */
#define HTTP_FLAG_TRAILER	0x02	/* Chunked encoding has trailer */
#define HTTP_FLAG_NOOP		0x04	/* No-op data (e.g. 100 Continue) */
#define HTTP_FLAG_TEXTMSG	0x08	/* Content is plain text, probably 
									   an error message */

/* 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_EXPECT, HTTP_HEADER_LAST
			 } HTTP_HEADER_TYPE;

/* HTTP header parsing information.  Note that the first letter must be 
   uppercase for the case-insensitive quick match */

typedef struct {
	const char *headerString;		/* Header string */
	const int headerStringLen;		/* Length of header string */
	const HTTP_HEADER_TYPE headerType;	/* Type corresponding to header string */
	} HTTP_HEADER_INFO;

static const FAR_BSS HTTP_HEADER_INFO httpHeaderInfo[] = {
	{ "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 },
	{ "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 */
	const char *httpStatusString;	/* String status value */
	const char *httpErrorString;	/* Text description of status */
	const int status;				/* Equivalent cryptlib status */
	} HTTP_STATUS_INFO;

static const FAR_BSS HTTP_STATUS_INFO httpStatusInfo[] = {
	{ 100, "100", "Continue", OK_SPECIAL },
	{ 101, "101", "Switching Protocols", CRYPT_ERROR_READ },
	{ 110, "110", "Warning: Response is stale", CRYPT_OK },
	{ 111, "111", "Warning: Revalidation failed", CRYPT_OK },
	{ 112, "112", "Warning: Disconnected operation", CRYPT_OK },
	{ 113, "113", "Warning: Heuristic expiration", CRYPT_OK },
	{ 199, "199", "Warning: Miscellaneous warning", CRYPT_OK },
	{ 200, "200", "OK", CRYPT_OK },
	{ 201, "201", "Created", CRYPT_ERROR_READ },
	{ 202, "202", "Accepted", CRYPT_ERROR_READ },
	{ 203, "203", "Non-Authoritative Information", CRYPT_OK },
	{ 204, "204", "No Content", CRYPT_ERROR_READ },
	{ 205, "205", "Reset Content", CRYPT_ERROR_READ },
	{ 206, "206", "Partial Content", CRYPT_ERROR_READ },
	{ 214, "214", "Warning: Transformation applied", CRYPT_OK },
	{ 250, "250", "RTSP: Low on Storage Space", CRYPT_OK },
	{ 299, "299", "Warning: Miscellaneous persistent warning", CRYPT_OK },
	{ 300, "300", "Multiple Choices", CRYPT_ERROR_READ },
	{ 301, "301", "Moved Permanently", OK_SPECIAL },
	{ 302, "302", "Moved Temporarily/Found", OK_SPECIAL },
	{ 303, "303", "See Other", CRYPT_ERROR_READ },
	{ 304, "304", "Not Modified", CRYPT_ERROR_READ },
	{ 305, "305", "Use Proxy", CRYPT_ERROR_READ },
	{ 306, "306", "Unused/obsolete", CRYPT_ERROR_READ },
	{ 307, "307", "Temporary Redirect", OK_SPECIAL },
	{ 400, "400", "Bad Request", CRYPT_ERROR_READ },
	{ 401, "401", "Unauthorized", CRYPT_ERROR_PERMISSION },
	{ 402, "402", "Payment Required", CRYPT_ERROR_READ },
	{ 403, "403", "Forbidden", CRYPT_ERROR_PERMISSION },
	{ 404, "404", "Not Found", CRYPT_ERROR_NOTFOUND },
	{ 405, "405", "Method Not Allowed", CRYPT_ERROR_NOTAVAIL },
	{ 406, "406", "Not Acceptable", CRYPT_ERROR_READ },
	{ 407, "407", "Proxy Authentication Required", CRYPT_ERROR_READ },
	{ 408, "408", "Request Time-out", CRYPT_ERROR_READ },
	{ 409, "409", "Conflict", CRYPT_ERROR_READ },
	{ 410, "410", "Gone", CRYPT_ERROR_NOTFOUND },
	{ 411, "411", "Length Required", CRYPT_ERROR_READ },
	{ 412, "412", "Precondition Failed", CRYPT_ERROR_READ },
	{ 413, "413", "Request Entity too Large", CRYPT_ERROR_OVERFLOW },
	{ 414, "414", "Request-URI too Large", CRYPT_ERROR_OVERFLOW },
	{ 415, "415", "Unsupported Media Type", CRYPT_ERROR_READ },
	{ 416, "416", "Requested range not satisfiable", CRYPT_ERROR_READ },
	{ 417, "417", "Expectation Failed", CRYPT_ERROR_READ },
	{ 426, "426", "Upgrade Required", CRYPT_ERROR_READ },
	{ 451, "451", "RTSP: Parameter not Understood", CRYPT_ERROR_BADDATA },
	{ 452, "452", "RTSP: Conference not Found", CRYPT_ERROR_NOTFOUND },
	{ 453, "453", "RTSP: Not enough Bandwidth", CRYPT_ERROR_NOTAVAIL },
	{ 454, "454", "RTSP: Session not Found", CRYPT_ERROR_NOTFOUND },
	{ 455, "455", "RTSP: Method not Valid in this State", CRYPT_ERROR_NOTAVAIL },
	{ 456, "456", "RTSP: Header Field not Valid for Resource", CRYPT_ERROR_NOTAVAIL },
	{ 457, "457", "RTSP: Invalid Range", CRYPT_ERROR_READ },
	{ 458, "458", "RTSP: Parameter is Read-Only", CRYPT_ERROR_PERMISSION },
	{ 459, "459", "RTSP: Aggregate Operation not Allowed", CRYPT_ERROR_PERMISSION },
	{ 460, "460", "RTSP: Only Aggregate Operation Allowed", CRYPT_ERROR_PERMISSION },
	{ 461, "461", "RTSP: Unsupported Transport", CRYPT_ERROR_NOTAVAIL },
	{ 462, "462", "RTSP: Destination Unreachable", CRYPT_ERROR_OPEN },
	{ 500, "500", "Internal Server Error", CRYPT_ERROR_READ },
	{ 501, "501", "Not Implemented", CRYPT_ERROR_NOTAVAIL },
	{ 502, "502", "Bad Gateway", CRYPT_ERROR_READ },
	{ 503, "503", "Service Unavailable", CRYPT_ERROR_READ },
	{ 504, "504", "Gateway Time-out", CRYPT_ERROR_READ },
	{ 505, "505", "HTTP Version not supported", CRYPT_ERROR_READ },
	{ 510, "510", "HTTP-Ext: Not Extended", CRYPT_ERROR_READ },
	{ 551, "551", "RTSP: Option not supported", CRYPT_ERROR_READ },
	{ 0, NULL, "Unknown HTTP status condition", CRYPT_ERROR_READ }
	};

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

/* 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 */

static int readLine( STREAM *stream, char *buffer, const int maxSize )
	{
	MIME_STATE state;
	int status;

	initMIMEstate( &state, maxSize );
	do
		{
		BYTE ch;

		status = stream->bufferedTransportReadFunction( stream, &ch, 1,
														TRANSPORT_FLAG_NONE );
		if( cryptStatusError( status ) )
			/* Network-level error, don't overwrite the extended error
			   information */
			return( status );
		status = addMIMEchar( &state, buffer, ch );
		}
	while( cryptStatusOK( status ) );
	if( cryptStatusError( status ) && status != OK_SPECIAL )
		/* We got an error other than an EOL condition, exit */
		retExtStream( stream, status, "Invalid HTTP header line" );
	return( endMIMEstate( &state ) );
	}

/* Skip whitespace in a line of text.  We only need to check for spaces as
   whitespace since it's been canonicalised already */

static const char *skipWhitespace( const char *buffer )
	{
	if( buffer == NULL )
		return( NULL );

	while( *buffer == ' ' )
		buffer++;
	return( *buffer ? buffer : NULL );
	}

/* Decode a hex nibble */

static int getNibble( const char srcCh )
	{
	int ch;

	ch = toLower( srcCh );
	if( !isXDigit( ch ) )
		return( CRYPT_ERROR_BADDATA );
	return( ( ch <= '9' ) ? ch - '0' : ch - ( 'a' - 10 ) );
	}

/* Encode/decode a string as per RFC 1866 (although the list of characters 
   that need to be escaped is itself given in RFC 2396).  Characters that 
   are permitted/not permitted are:

	 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
	x..x.xx....x...xxxxxxxxxxxx.xxxxx

   Because of this it's easier to check for the most likely permitted
   characters (alphanumerics), and then to check for any special-case
   chars */

static void encodeRFC1866( STREAM *headerStream, const char *source )
	{
	static const char *allowedChars = "$-_.!*'(),\"/";	/* RFC 1738 + '/' */

	while( *source )
		{
		const int ch = *source++;
		int i;

		if( isAlnum( ch ) )
			{
			sputc( headerStream, ch );
			continue;
			}
		if( ch == ' ' )
			{
			sputc( headerStream, '+' );
			continue;
			}
		for( i = 0; allowedChars[ i ] && ch != allowedChars[ i ]; i++ );
		if( allowedChars[ i ] )
			/* It's in the allowed-chars list, output it verbatim */
			sputc( headerStream, ch );
		else
			{
			char escapeString[ 8 ];
			int escapeStringLen;

			/* It's a special char, escape it */
			escapeStringLen = sPrintf( escapeString, "%%%02X", ch );
			swrite( headerStream, escapeString, escapeStringLen );
			}
		}
	}

static int decodeRFC1866( char *dest, const char *source, 
						  const int sourceLen )
	{
	const char *origDestPtr = dest;
	BOOLEAN seenEscape = FALSE;
	int i;

	for( i = 0; i < sourceLen; i++ )
		{
		int chLo, chHi, ch;

		/* If it's not an escape, just copy it straight over */
		if( *source != '%' )
			{
			*dest++ = *source++;
			continue;
			}
		source++;	/* Skip '%' */
		i += 2;		/* Skip escape sequence */
		seenEscape = TRUE;

		/* Decode the escaped character */
		if( sourceLen - i < 1 )
			return( CRYPT_ERROR_BADDATA );
		chHi = getNibble( *source ); source++;
		chLo = getNibble( *source ); source++;
		if( cryptStatusError( chHi ) || cryptStatusError( chLo ) )
			return( CRYPT_ERROR_BADDATA );
		ch = ( chHi << 4 ) | chLo;
		if( !isPrint( ch ) )
			/* 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) */
			return( CRYPT_ERROR_BADDATA );
		*dest++ = ch;
		}

	/* If we've seen an escape sequence, tell the caller the new length, 
	   otherwise tell them that nothing's changed */
	return( seenEscape ? dest - origDestPtr : OK_SPECIAL );
	}

/* Convert a hex ASCII string used with chunked encoding into a numeric
   value */

static int getChunkLength( const char *buffer, const int bufLen )
	{
	int i, chunkLength = 0, length = bufLen;

	/* Chunk size information can have extensions tacked onto it following a 
	   ';', strip these before we start */
	for( i = 0; i < bufLen; i++ )
		if( buffer[ i ] == ';' )
			{
			/* Move back to the end of the string that precedes the ';' */
			while( i > 0 && buffer[ i - 1 ] == ' ' )
				i--;
			length = i;
			break;
			}

	/* 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 */
	for( i = 0; i < length; i++ )
		{
		const int ch = getNibble( buffer[ i ] );

		if( cryptStatusError( ch ) )
			return( CRYPT_ERROR_BADDATA );
		chunkLength = ( chunkLength << 4 ) | ch;
		}

	return( chunkLength );
	}

/* If we time out when sending HTTP header data this would usually be 

⌨️ 快捷键说明

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