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

📄 net_http.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 3 页
字号:
		 i++ );
	httpStatusPtr = &httpStatusInfo[ i ];
	if( httpStatus != NULL )
		*httpStatus = aToI( lineBufPtr );
	if( httpStatusPtr->status == OK_SPECIAL )
		/* It's a special-case condition such as a redirect, tell the caller
		   to handle it specially */
		return( OK_SPECIAL );
	if( httpStatusPtr->status != CRYPT_OK )
		{
		/* It's an error condition, return extended error info */
		assert( httpStatusPtr->httpStatusString != NULL );
							/* Catch oddball errors in debug version */
		retExtStream( stream, httpStatusPtr->status, "HTTP status: %s",
					  httpStatusPtr->httpErrorString );
		}
	return( CRYPT_OK );
	}

/* Process an HTTP header line looking for anything that we can handle */

static int checkHeaderLine( char **lineBufPtrPtr,
							HTTP_HEADER_TYPE *headerType, void *stream )
	{
	const HTTP_HEADER_INFO *headerInfoPtr;
	const char *lineBufPtr = *lineBufPtrPtr;
	const char firstChar = toUpper( *lineBufPtr );
	const int lineLength = strlen( lineBufPtr );
	int i;

	/* Clear return value */
	*headerType = HTTP_HEADER_NONE;

	/* Look for a header line that we recognise */
	for( i = 0;
		 httpHeaderInfo[ i ].headerString != NULL && \
		 ( httpHeaderInfo[ i ].headerString[ 0 ] != firstChar || \
		   lineLength < httpHeaderInfo[ i ].headerStringLen || \
		   strCompare( lineBufPtr, httpHeaderInfo[ i ].headerString, \
					   httpHeaderInfo[ i ].headerStringLen ) );
		 i++ );
	headerInfoPtr = &httpHeaderInfo[ i ];
	if( headerInfoPtr->headerString == NULL )
		/* It's nothing that we can handle, exit */
		return( CRYPT_OK );

	/* Make sure that there's a token present */
	lineBufPtr = skipWhitespace( lineBufPtr + headerInfoPtr->headerStringLen );
	if( lineBufPtr == NULL )
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Missing HTTP header token for '%s'",
					  headerInfoPtr->headerString );

	/* Tell the caller what we found */
	*lineBufPtrPtr = ( char * ) lineBufPtr;
	*headerType = headerInfoPtr->headerType;
	return( CRYPT_OK );
	}

/* Read the first line in an HTTP response header */

static int readFirstHeaderLine( STREAM *stream, int *httpStatus,
								char *lineBuffer, const int maxLength )
	{
	int status;

	*httpStatus = CRYPT_OK;

	/* Read the header and check for an HTTP ID */
	status = readLine( stream, lineBuffer, maxLength );
	if( cryptStatusError( status ) )
		return( status );
	status = checkHTTPID( stream, lineBuffer, status );
	if( cryptStatusError( status ) )
		retExtStream( stream, status, "Invalid HTTP ID/version" );

	/* Read the HTTP status info */
	return( readHTTPStatus( stream, httpStatus, lineBuffer + status ) );
	}

/* Read the remaining HTTP header lines after the first one */

static int readHeaderLines( STREAM *stream, char *lineBuffer,
							int *contentLength, int *httpErrorStatus,
							int *flags, const int minLength,
							const int maxLength, const BOOLEAN expandBuffer )
	{
	BOOLEAN seenHost = FALSE, seenLength = FALSE;
	int localLength = 0, lineCount, status;

	/* Clear return value */
	if( httpErrorStatus != NULL )
		*httpErrorStatus = 0;
	if( contentLength != NULL )
		*contentLength = 0;

	/* Read each line in the header checking for any fields that we need to
	   handle */
	for( lineCount = 0; lineCount < MAX_HEADER_LINES; lineCount++ )
		{
		HTTP_HEADER_TYPE headerType;
		char *lineBufPtr = lineBuffer;

		status = readLine( stream, lineBuffer, maxLength );
		if( cryptStatusError( status ) )
			return( status );
		if( status == 0 )
			/* End of input, exit */
			break;
		status = checkHeaderLine( &lineBufPtr, &headerType, stream );
		if( cryptStatusError( status ) )
			return( status );
		switch( headerType )
			{
			case HTTP_HEADER_HOST:
				/* Remember that we've seen a Host: line, to meet the HTTP
				   1.1 requirements */
				seenHost = TRUE;
				break;

			case HTTP_HEADER_CONTENT_LENGTH:
				/* Get the content length.  At this point all we do is a
				   general sanity check that the length looks OK, a specific
				   check against the caller-supplied minimum allowable
				   length is performed later since the content length may
				   also be provided as a chunked encoding length */
				localLength = aToI( lineBufPtr );
				if( localLength <= 0 )
					retExtStream( stream, CRYPT_ERROR_BADDATA,
								  "Invalid HTTP content length %d",
								  localLength );
				seenLength = TRUE;
				break;

			case HTTP_HEADER_CONTENT_TYPE:
				/* Sometimes if there's an error it'll be returned at the
				   HTTP level rather than at the tunneled-over-HTTP protocol
				   level.  The easiest way to check for this would be to
				   make sure that the content-type matches the expected type
				   and report anything else as an error.  Unfortunately due
				   to the hit-and-miss handling of content-types by PKI
				   software using HTTP as a substrate it's not safe to do
				   this, so we have to default to allow-all rather than deny-
				   all, treating only straight text as a problem type
				   (although there are probably also apps out there
				   somewhere that send their PKI messages marked as plain
				   text) */
				if( !strCompare( lineBufPtr, "text/", 5 ) )
					*flags |= HTTP_FLAG_ERRORMSG;
				break;

			case HTTP_HEADER_TRANSFER_ENCODING:
				if( !strCompare( lineBuffer, "Chunked", 7 ) )
					retExtStream( stream, CRYPT_ERROR_BADDATA,
								  "Invalid HTTP transfer encoding method, "
								  "expected 'Chunked'" );

				/* If it's a chunked encoding, the length is part of the
				   data and must be read later */
				*flags |= HTTP_FLAG_CHUNKED;
				break;

			case HTTP_HEADER_CONTENT_ENCODING:
				/* We can't handle any type of content encoding (e.g. gzip,
				   compress, deflate) except the no-op identity encoding */
				if( !strCompare( lineBuffer, "Identity", 8 ) )
					{
					if( httpErrorStatus != NULL )
						*httpErrorStatus = 415;
					lineBuffer[ CRYPT_MAX_TEXTSIZE ] = '\0';
					retExtStream( stream, CRYPT_ERROR_BADDATA,
								  "Invalid HTTP content encoding method '%s'",
								  lineBuffer );
					}
				break;

			case HTTP_HEADER_CONTENT_TRANSFER_ENCODING:
				/* HTTP uses Transfer-Encoding, not the MIME Content-
				   Transfer-Encoding types such as base64 or quoted-
				   printable.  If any implementations use a C-T-E, we make
				   sure it's something that we can handle */
				if( !strCompare( lineBuffer, "Identity", 8 ) && \
					!strCompare( lineBuffer, "Binary", 6 ) )
					{
					if( httpErrorStatus != NULL )
						*httpErrorStatus = 415;
					lineBuffer[ CRYPT_MAX_TEXTSIZE ] = '\0';
					retExtStream( stream, CRYPT_ERROR_BADDATA,
								  "Invalid HTTP content transfer encoding "
								  "method '%s'", lineBuffer );
					}
				break;

			case HTTP_HEADER_TRAILER:
				/* The body is followed by trailer lines, used with chunked
				   encoding where some header lines can't be produced until
				   the entire body has been generated.  This wasn't added
				   until RFC 2616, since many implementations are based on
				   RFC 2068 and don't produce this header we don't do
				   anything with it (the trailer can be auto-detected
				   anyway, it's only present to tell the receiver to perform
				   certain actions such as creating an MD5 hash of the data
				   as it arrives) */
				*flags |= HTTP_FLAG_TRAILER;
				break;

			case HTTP_HEADER_CONNECTION:
				/* If the other side has indicated that it's going to close
				   the connection, remember that the stream is now no longer
				   usable */
				if( !strCompare( lineBuffer, "Close", 5 ) )
					sioctl( stream, STREAM_IOCTL_CONNSTATE, NULL, FALSE );
				break;

			case HTTP_HEADER_WARNING:
				/* Read the HTTP status info from the warning, discarding any
				   error status since this isn't an error */
				readHTTPStatus( stream, NULL, lineBufPtr );
				break;

			case HTTP_HEADER_EXPECT:
				/* If the other side wants the go-ahead to continue, give it
				   to them.  We do this automatically because we're merely
				   using HTTP as a substrate, the real decision will be made
				   at the higher-level protocol layer.  In theory we could
				   at least check the content type, but see the comment in
				   the content-type handler for why we don't do this */
				if( !strCompare( lineBuffer, "100-Continue", 12 ) )
					sendHTTPError( stream, lineBuffer, 100 );
				break;

			case HTTP_HEADER_NONE:
				/* It's something that we don't know/care about, skip it */
				break;

			default:
				assert( NOTREACHED );
			}
		}
	if( lineCount >= MAX_HEADER_LINES )
		retExtStream( stream, CRYPT_ERROR_OVERFLOW,
					  "Too many HTTP header lines" );

	/* If it's a chunked encoding for which the length is kludged on before
	   the data as a hex string, decode the length value */
	if( *flags & HTTP_FLAG_CHUNKED )
		{
		status = readLine( stream, lineBuffer, maxLength );
		if( cryptStatusError( status ) )
			return( status );
		status = localLength = getChunkLength( lineBuffer, status );
		if( cryptStatusError( status ) )
			retExtStream( stream, CRYPT_ERROR_BADDATA,
						  "Invalid length for HTTP chunked encoding" );
		seenLength = TRUE;
		}

	/* If this is a no-op read (for example lines following a 100 Continue
	   response), all we're interested in is draining the input, so we don't
	   check any further */
	if( *flags & HTTP_FLAG_NOOP )
		return( CRYPT_OK );

	/* Make sure that we've been given a length.  In theory a server could
	   indicate the length implicitly by closing the connection once it's
	   sent the last byte, but this isn't allowed for PKI messages.  The
	   client can't use this option in any case since that would make it
	   impossible for us to send back the response */
	if( !seenLength )
		{
		if( httpErrorStatus != NULL )
			*httpErrorStatus = 411;
		retExtStream( stream, CRYPT_ERROR_BADDATA, "Missing HTTP length" );
		}

	/* Make sure that the length is sensible */
	if( localLength < minLength )
		retExtStream( stream, CRYPT_ERROR_UNDERFLOW,
					  "Insufficient HTTP content data" );
	if( !expandBuffer && ( localLength > maxLength ) )
		{
		if( httpErrorStatus != NULL )
			*httpErrorStatus = 413;
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Excessive HTTP content data" );
		}
	if( contentLength != NULL )
		*contentLength = localLength;

	/* If we're a server talking HTTP 1.1 and we haven't seen a Host: header
	   from the client, return an error */
	if( ( stream->flags & STREAM_NFLAG_ISSERVER ) && \
		!isHTTP10( stream ) && !seenHost )
		{
		if( httpErrorStatus != NULL )
			*httpErrorStatus = 400;
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Missing HTTP host header" );
		}

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Read/write Request Header						*
*																			*
****************************************************************************/

/* Write an HTTP request header */

static int writeRequestHeader( STREAM *stream, const int length )
	{
	STREAM headerStream;
	char headerBuffer[ HTTP_LINEBUF_SIZE ];
	const int transportFlag = length ? TRANSPORT_FLAG_NONE : \
									   TRANSPORT_FLAG_FLUSH;
	const int hostLen = strlen( stream->host );
	int headerLength;

	sMemOpen( &headerStream, headerBuffer, HTTP_LINEBUF_SIZE );
	if( length > 0 )
		swrite( &headerStream, "POST ", 5 );
	else
		swrite( &headerStream, "GET ", 4 );
	if( stream->flags & STREAM_NFLAG_HTTPPROXY )
		{
		/* If we're going through an HTTP proxy, send an absolute URL rather
		   than just the relative location */
		swrite( &headerStream, "http://", 7 );
		swrite( &headerStream, stream->host, hostLen );
		if( stream->port != 80 )
			{
			char portString[ 16 ];
			int portStringLength;

			portStringLength = sprintf( portString, ":%d", stream->port );
			swrite( &headerStream, portString, portStringLength );
			}
		}
	if( stream->path != NULL )
		swrite( &headerStream, stream->path, strlen( stream->path ) );
	else
		sputc( &headerStream, '/' );
	if( stream->query != NULL )
		{
		sputc( &headerStream, '?' );
		encodeRFC1866( &headerStream, stream->query );
		}
	if( isHTTP10( stream ) )
		swrite( &headerStream, " HTTP/1.0\r\n", 11 );
	else
		{
		swrite( &headerStream, " HTTP/1.1\r\nHost: ", 17 );
		swrite( &headerStream, stream->host, hostLen );
		swrite( &headerStream, "\r\n", 2 );
		if( stream->flags & STREAM_NFLAG_LASTMSG )
			swrite( &headerStream, "Connection: close\r\n", 19 );
		}
	if( length > 0 )
		{
		char lengthString[ 16 ];
		int lengthStringLength;

		swrite( &headerStream, "Content-Type: ", 14 );
		swrite( &headerStream, stream->contentType,
				strlen( stream->contentType ) );
		swrite( &headerStream, "\r\nContent-Length: ", 18 );
		lengthStringLength = sprintf( lengthString, "%d", length );
		swrite( &headerStream, lengthString, lengthStringLength );
		swrite( &headerStream, "\r\nCache-Control: no-cache\r\n", 27 );
		}
	swrite( &headerStream, "\r\n", 2 );
	headerLength = stell( &headerStream );
	assert( sStatusOK( &headerStream ) );
	sMemDisconnect( &headerStream );

⌨️ 快捷键说明

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