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

📄 net_http.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*						cryptlib HTTP Interface Routines					*
*						Copyright Peter Gutmann 1998-2003					*
*																			*
****************************************************************************/

#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 "../misc/stream.h"
#else
  #include "crypt.h"
  #include "misc/stream.h"
#endif /* Compiler-specific includes */

#ifdef USE_TCP

/* 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.  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_ERRORMSG	0x08	/* Content is text 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 */

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 */
	{ "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 },
	{ 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 );
	}

/* Encode 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 ] )
			sputc( headerStream, ch );
		else
			{
			char escapeString[ 8 ];
			int escapeStringLen;

			escapeStringLen = sPrintf( escapeString, "%%%02X", ch );
			swrite( headerStream, escapeString, escapeStringLen );
			}
		}
	}

/* 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 = toLower( buffer[ i ] );

		if( !isXDigit( ch ) )
			return( CRYPT_ERROR_BADDATA );
		chunkLength = ( chunkLength << 4 ) | \
					  ( ( ch <= '9' ) ? ch - '0' : ch - ( 'a' - 10 ) );
		}

	return( chunkLength );
	}

/* Send an HTTP error message */

static int sendHTTPError( STREAM *stream, char *headerBuffer,
						  const int httpStatus )
	{
	const char *statusString = "400";
	const char *errorString = "Bad Request";
	int length, i;

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

	/* Send the error message to the peer */
	length = sPrintf( headerBuffer, "%s %s %s\r\n\r\n",
					  isHTTP10( stream ) ? "HTTP/1.0" : "HTTP/1.1",
					  statusString, errorString );
	return( stream->bufferedTransportWriteFunction( stream, headerBuffer, length,
													TRANSPORT_FLAG_FLUSH ) );
	}

/****************************************************************************
*																			*
*							HTTP Parsing Functions							*
*																			*
****************************************************************************/

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

static int checkHTTPID( STREAM *stream, const char *buffer, const int length )
	{
	if( length < 8 || strCompare( buffer, "HTTP/1.", 7 ) )
		return( CRYPT_ERROR_BADDATA );
	if( buffer[ 7 ] == '0' )
		stream->flags |= STREAM_NFLAG_HTTP10;
	else
		if( buffer[ 7 ] != '1' )
			return( CRYPT_ERROR_BADDATA );

	return( 8 );
	}

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

static int readHTTPStatus( STREAM *stream, int *httpStatus,
						   const char *lineBuffer )
	{
	const HTTP_STATUS_INFO *httpStatusPtr;
	const char *lineBufPtr;
	int i;

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

	/* Process the HTTP status code and translate it into a cryptlib
	   equivalent.  Most of the HTTP codes don't have any meaning in a
	   cryptlib context, so we return a generic CRYPT_ERROR_READ */
	lineBufPtr = skipWhitespace( lineBuffer );
	if( lineBufPtr == NULL || strlen( lineBufPtr ) < 3 || \
		!isDigit( *lineBufPtr ) )
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Invalid/missing HTTP status code" );
	for( i = 0; \
		 httpStatusInfo[ i ].httpStatus && \
		 strCompare( lineBufPtr, httpStatusInfo[ i ].httpStatusString, 3 ); \

⌨️ 快捷键说明

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