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

📄 http.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
	return( status );
	}

/****************************************************************************
*																			*
*							Read/write Response Header						*
*																			*
****************************************************************************/

/* Write an HTTP response header */

static int writeResponseHeader( STREAM *stream, const int length )
	{
	char headerBuffer[ HTTP_LINEBUF_SIZE ];
	int headerPos;

	/* We don't use a stream to encode the header lines for responses since
	   all of the lines are quite short and can't overflow the buffer */
	if( isHTTP10( stream ) )
		strcpy( headerBuffer, "HTTP/1.0 200 OK\r\n" );
	else
		{
		strcpy( headerBuffer, "HTTP/1.1 200 OK\r\n" );
		if( stream->flags & STREAM_NFLAG_LASTMSG )
			strcat( headerBuffer, "Connection: close\r\n" );
		}
	headerPos = strlen( headerBuffer );
	strcat( headerBuffer + headerPos, "Content-Type: " );
	strcat( headerBuffer + headerPos, stream->contentType );
	strcat( headerBuffer + headerPos, "\r\nContent-Length: " );
	headerPos += strlen( headerBuffer + headerPos );
	sPrintf( headerBuffer + headerPos, 
			 "%d\r\nCache-Control: no-cache\r\n", length );
	if( isHTTP10( stream ) )
		strcat( headerBuffer + headerPos, "Pragma: no-cache\r\n" );
	strcat( headerBuffer + headerPos, "\r\n" );
	headerPos = strlen( headerBuffer );

	return( sendHTTPData( stream, headerBuffer, headerPos,
						  TRANSPORT_FLAG_NONE ) );
	}

/* Read an HTTP response header */

static int readResponseHeader( STREAM *stream, int *contentLength, char *buffer,
							   const int maxLength,
							   const BOOLEAN expandBuffer, int *flags )
	{
	int repeatCount, status;

	/* Clear return value */
	*contentLength = CRYPT_ERROR;

	/* If it's a stateless HTTP read, we need to send the fetch request
	   before we can read anything back */
	if( stream->protocol == STREAM_PROTOCOL_HTTP )
		{
		assert( !*stream->contentType );

		status = writeRequestHeader( stream, 0 );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Read the returned response header from the server, taking various
	   special-case conditions into account.  In theory we could also handle
	   the 503 "Retry-After" status, but there's no sensible reason why
	   anyone should send us this, and even if they do it'll screw up a lot
	   of the PKI protocols, which have timeliness constraints built in */
	for( repeatCount = 0; repeatCount < MAX_RETRY_COUNT; repeatCount++ )
		{
		BOOLEAN needsSpecialHandling = FALSE;
		int httpStatus;

		/* Read the response header */
		status = readFirstHeaderLine( stream, &httpStatus, buffer, maxLength );
		if( status == OK_SPECIAL )
			{
			/* If it's a special-case header (e.g. a 100 Continue), turn the
			   read into a no-op read that drains the input to get to the
			   real data */
			*flags |= HTTP_FLAG_NOOP;
			needsSpecialHandling = TRUE;
			}
		else
			if( cryptStatusError( status ) )
				{
				int localFlags = *flags | HTTP_FLAG_NOOP;

				/* Drain the input and exit */
				readHeaderLines( stream, buffer, NULL, NULL, &localFlags, 
								 5, maxLength, FALSE );
				return( status );
				}

		/* Process the remaining header lines.  5 bytes is the minimum-size
		   object that can be returned from any HTTP-based message which is
		   exchanged by cryptlib, this being an OCSP response containing a
		   single-byte status value, i.e. SEQUENCE { ENUM x } */
		status = readHeaderLines( stream, buffer, contentLength, NULL, 
								  flags, 5, maxLength, expandBuffer );
		*flags &= ~HTTP_FLAG_NOOP;
		if( cryptStatusError( status ) )
			return( status );

		/* If it's not something like a redirect that needs special-case 
		   handling, we're done */
		if( !needsSpecialHandling )
			return( CRYPT_OK );

		assert( httpStatus == 100 || httpStatus == 301 || \
				httpStatus == 302 || httpStatus == 307 );

		/* If we got a 100 Continue response, try for another header that
		   follows the first one */
		if( httpStatus == 100 )
			continue;

		/* If we got a 301, 302, or 307 Redirect then in theory we should 
		   proceed roughly as per the code below, however in practice it's 
		   not nearly as simple as this, because what we're in effect doing 
		   is taking a stream and replacing it with a completely new stream 
		   (different host/abs-path/query info, new socket with optional 
		   proxy handling, etc etc).  One way to do this would be to read 
		   the new location into the current stream buffer and pass it back 
		   with a special status telling the stream-level code to create a 
		   new stream, clean up the old one, and perform a deep copy of the 
		   new stream over to the old one.  We'll leave this for a time when 
		   it's really needed.
		   
		   In addition the semantics of the following don't quite follow 
		   those of RFC 2616 because of the HTTP-as-a-substrate use rather
		   than direct use in a browser.  Specifically, anything other than
		   a GET for a 302 or 307 isn't supposed to perform an automatic 
		   redirect without asking the user, because of concerns that it'll
		   change the semantics of the request.  However, since we're not an
		   interactive web browser there's no way that we can ask a user for 
		   redirect permission, and in any case since we're merely using 
		   HTTP as a substrate for a cryptographically protected PKI 
		   message (and specifically assuming that the HTTP layer is 
		   completely insecure), any problems will be caught by the crypto
		   protocol layer */
#if 0
		if( !*location )
			return( CRYPT_ERROR_READ );
		stream->closeSocketFunction( stream );
		clFree( "readResponseHeader", stream->host );
		stream->host = NULL;
		status = parseLocation( stream, location );
		if( cryptStatusError( status ) )
			return( CRYPT_ERROR_READ );
#endif /* 0 */
		retExtStream( stream, CRYPT_ERROR_READ,
					  "Unable to process HTTP 301/302 redirect" );
		}

	/* We used up our maximum number of retries, bail out */
	retExtStream( stream, CRYPT_ERROR_READ,
				  "HTTP retry/redirection loop detected" );
	}

/****************************************************************************
*																			*
*							HTTP Access Functions							*
*																			*
****************************************************************************/

/* Read data from an HTTP stream */

static int readFunction( STREAM *stream, void *buffer, int length )
	{
	void *bufPtr = buffer;
	int flags = HTTP_FLAG_NONE, contentLength, readLength, status;

	/* Read the HTTP packet header and adjust the read buffer size if
	   necessary.  This adjustment only occurs on the client side, which 
	   needs to be able to handle arbitrary-length responses from the 
	   server */
	if( stream->flags & STREAM_NFLAG_ISSERVER )
		status = readRequestHeader( stream, &contentLength, buffer, length,
									&flags );
	else
		status = readResponseHeader( stream, &contentLength, buffer, length,
									 ( stream->callbackFunction != NULL ) ? \
									 TRUE : FALSE, &flags );
	if( cryptStatusError( status ) )
		return( status );
	if( contentLength > length )
		{
		if( stream->callbackFunction != NULL )
			{
			/* There's a buffer-adjust callback present, try and increase the
			   buffer size */
			assert( stream->callbackParams != NULL );
			status = stream->callbackFunction( stream->callbackParams,
											   &bufPtr, contentLength );
			if( cryptStatusError( status ) )
				return( status );
			assert( isWritePtr( bufPtr, contentLength ) );
			}
		else
			return( CRYPT_ERROR_OVERFLOW );
		}

	/* If it's an idempotent read, all the information was contained in the 
	   header and we're done */
	if( stream->flags & STREAM_NFLAG_IDEMPOTENT )
		return( contentLength );

	/* Read the payload data from the client/server */
	readLength = status = \
		stream->bufferedTransportReadFunction( stream, bufPtr, contentLength,
											   TRANSPORT_FLAG_NONE );
	if( cryptStatusError( status ) )
		return( status );
	if( readLength < contentLength )
		/* We timed out before reading all the data.  Usually this will be
		   reported as a CRYPT_ERROR_TIMEOUT by the lower-level read
		   routines, however due to the multiple layers of I/O and special
		   case timeout handling when (for example) a cryptlib transport
		   session is layered over the network I/O layer, we perform an
		   explicit check here to make sure that we got everything */
		retExtStream( stream, CRYPT_ERROR_TIMEOUT,
					  "HTTP read timed out before all data could be read" );

	/* If it's a plain-text error message, return it to the caller */
	if( flags & HTTP_FLAG_TEXTMSG )
		{
		BYTE *byteBufPtr = bufPtr;

		/* Usually a body returned as plain text is an error message that
		   (for some reason) is sent as content rather than an HTTP error,
		   however in some unusual cases the content will be the requested
		   object marked as plain text.  This only seems to occur with 
		   straight HTTP fetches from misconfigured servers rather than when 
		   HTTP is being used as a tunnelling mechanism for a PKI protocol, 
		   so we can filter this by requiring that the fetch is a straight 
		   HTTP fetch (not a request/response PKI protocol fetch), that the 
		   request is over a minimum size (most error messages are quite 
		   short), and that the first bytes match what would be seen in a
		   PKI object such as a cert or CRL */
		if( stream->protocol != STREAM_PROTOCOL_HTTP || \
			contentLength < 256 || ( byteBufPtr[ 0 ] != 0x30 ) || \
			!( byteBufPtr[ 1 ] & 0x80 ) || \
			( isAlpha( byteBufPtr[ 2 ] ) && isAlpha( byteBufPtr[ 3 ] ) && \
			  isAlpha( byteBufPtr[ 4 ] ) ) )
			{
			byteBufPtr[ min( readLength, MAX_ERRMSG_SIZE - 32 ) ] = '\0';
			retExtStream( stream, CRYPT_ERROR_READ,
						  "HTTP server reported: '%s'", 
						  sanitiseString( buffer ) );
			}
		}

	/* If we're reading chunked data, drain the input by processing the
	   trailer.  The reason why there can be extra header lines at the end 
	   of the chunked data is because it's designed to be an indefinite-
	   length streamable format that doesn't require buffering the entire 
	   message before emitting it.  Since some header information may not be
	   available until the entire message has been generated, the HTTP spec.
	   makes provisions for adding further header lines as a trailer.  In
	   theory we should check for the HTTP_FLAG_TRAILER flag before reading
	   trailer lines rather than just swallowing the last CRLF, however the
	   "Trailer:" header wasn't added until RFC 2616 (RFC 2068 didn't have
	   it) so we can't rely on its presence:

			CRLF
			"0" CRLF
			trailer-lines*
			CRLF

	   Normally we wouldn't have to worry about trailer data, but if it's an 
	   HTTP 1.1 persistent connection we need to clear the way for the next 
	   lot of data */
	if( flags & HTTP_FLAG_CHUNKED )
		{
		char headerBuffer[ HTTP_LINEBUF_SIZE + 8 ];
		int noopFlags = HTTP_FLAG_NOOP;

		status = readLine( stream, headerBuffer, HTTP_LINEBUF_SIZE );
		if( !cryptStatusError( status ) )
			status = readLine( stream, headerBuffer, HTTP_LINEBUF_SIZE );
		if( cryptStatusError( status ) )
			return( status );
		status = getChunkLength( headerBuffer, status );
		if( status != 0 )
			retExtStream( stream, CRYPT_ERROR_BADDATA,
						  "Unexpected additional data in HTTP chunked data" );
		status = readHeaderLines( stream, headerBuffer, NULL, NULL, 
								  &noopFlags, 0, HTTP_LINEBUF_SIZE, FALSE );
		}

	return( cryptStatusError( status ) ? status : readLength );
	}

/* Write data to an HTTP stream */

static int writeFunction( STREAM *stream, const void *buffer,
						  const int length )
	{
	int localLength = length, status;

	/* Send the out-of-band HTTP header data to the client or server */
	if( stream->flags & STREAM_NFLAG_ISSERVER )
		{
		/* If it's an idempotent get, decode the returned data */
		if( stream->flags & STREAM_NFLAG_IDEMPOTENT )
			{
			const BYTE *bufPtr = buffer;

			status = ( short int ) mgetWord( bufPtr );
			if( cryptStatusError( status ) )
				{
				char headerBuffer[ HTTP_LINEBUF_SIZE ];

				/* It's an error status response, send the translated
				   error status and exit.  We have to map the send return
				   value to a written byte count to avoid triggering the
				   incomplete-write check at the higher level */
				status = sendHTTPError( stream, headerBuffer, 
							( status == CRYPT_ERROR_NOTFOUND ) ? 404 : \
							( status == CRYPT_ERROR_PERMISSION ) ? 401 : \
																400 );
				return( cryptStatusError( status ) ? status : length );
				}
			buffer = bufPtr;
			localLength -= 2;
			}

		status = writeResponseHeader( stream, localLength );
		}
	else
		{
		assert( ( stream->flags & STREAM_NFLAG_HTTPTUNNEL ) || \
				strlen( stream->contentType ) );
		assert( !( ( stream->flags & STREAM_NFLAG_HTTPPROXY ) && 
				   ( stream->flags & STREAM_NFLAG_HTTPTUNNEL ) ) );
		assert( stream->host != NULL );

		status = writeRequestHeader( stream, localLength );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* Send the payload data to the client/server.  Since we may have 
	   modified the length of the data being written we have to be careful 
	   to return the correct amount to avoid triggering incomplete-write 
	   checks */
	status = stream->bufferedTransportWriteFunction( stream, buffer, localLength,
													 TRANSPORT_FLAG_FLUSH );
	return( ( status == localLength ) ? length : status );
	}

int setStreamLayerHTTP( STREAM *stream )
	{
	/* Set the access method pointers */
	stream->writeFunction = writeFunction;
	stream->readFunction = readFunction;

	/* HTTP provides its own data-size and flow-control indicators so we 
	   don't want the higher-level code to try and do this for us */
	stream->flags |= STREAM_NFLAG_ENCAPS;

	return( CRYPT_OK );
	}
#endif /* USE_HTTP */

⌨️ 快捷键说明

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