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

📄 net_http.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 3 页
字号:
	return( stream->bufferedTransportWriteFunction( stream, headerBuffer,
													headerLength,
													transportFlag ) );
	}

/* Read an HTTP request header */

static int readRequestHeader( STREAM *stream, int *contentLength,
							  char *buffer, const int maxLength, int *flags )
	{
	const char *lineBufPtr;
	int httpStatus, status;

	assert( stream->flags & STREAM_NFLAG_ISSERVER );

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

	/* Read the POST header and check for "POST x HTTP/1.x" (=15).  In theory
	   this could be a bit risky because the original CERN server required an
	   extra (spurious) CRLF after a POST, so that various early clients sent
	   an extra CRLF that isn't included in the Content-Length header and
	   ends up preceding the start of the next load of data.  We don't check
	   for this because it only applies to very old pure-HTTP (rather than
	   HTTP-as-a-transport-layer) clients, which are unlikely to be hitting a
	   PKI responder */
	status = readLine( stream, buffer, maxLength );
	if( cryptStatusError( status ) )
		{
		/* If it's an HTTP-level error (e.g. line too long), send back an
		   error response */
		if( status != CRYPT_ERROR_COMPLETE )
			sendHTTPError( stream, buffer,
						   ( status == CRYPT_ERROR_OVERFLOW ) ? 414 : 400 );
		return( status );
		}
	if( strCompare( buffer, "POST", 4 ) )
		{
		sendHTTPError( stream, buffer, 501 );
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Invalid HTTP request, expected 'POST'" );
		}
	lineBufPtr = buffer + 4;

	/* Skip ' '* * ' '* and check for the HTTP ID.  We don't care what the
	   location is since it's not relevant for anything, this also avoids
	   complications with absolute vs.relative URLs, character encoding/
	   escape sequences, and so on */
	if( ( lineBufPtr = skipWhitespace( lineBufPtr ) ) == NULL )
		{
		sendHTTPError( stream, buffer, 400 );
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Missing HTTP URI" );
		}
	while( *lineBufPtr && *lineBufPtr != ' ' )
		lineBufPtr++;
	if( ( lineBufPtr = skipWhitespace( lineBufPtr ) ) == NULL )
		{
		sendHTTPError( stream, buffer, 400 );
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Missing HTTP ID/version" );
		}
	status = checkHTTPID( stream, lineBufPtr, strlen( lineBufPtr ) );
	if( cryptStatusError( status ) )
		{
		sendHTTPError( stream, buffer, 505 );
		retExtStream( stream, status, "Invalid HTTP ID/version" );
		}

	/* Process the remaining header lines.  ~32 bytes is the minimum-size
	   object that can be returned from any HTTP-based message which is
	   exchanged by cryptlib, this being a TSP request */
	status = readHeaderLines( stream, buffer, contentLength, &httpStatus,
							  flags, 32, maxLength, FALSE );
	if( cryptStatusError( status ) )
		/* We always (try and) send an HTTP error response once we get to
		   this stage since chances are it'll be a problem with an HTTP
		   header rather than a low-level network read problem */
		sendHTTPError( stream, buffer, httpStatus );
	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" );
	return( stream->bufferedTransportWriteFunction( stream, headerBuffer,
													strlen( headerBuffer ),
													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 ) )
				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 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 );
		}

	/* 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_READ,
					  "HTTP read timed out before all data could be read" );

	/* If it's an error message, return it to the caller */
	if( flags & HTTP_FLAG_ERRORMSG )
		{
		( ( char * ) buffer )[ min( readLength, MAX_ERRMSG_SIZE - 32 ) ] = '\0';

		retExtStream( stream, CRYPT_ERROR_READ,
					  "HTTP server reported: '%s'", 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 ];
		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 status;

	/* Send the out-of-band HTTP header data to the client or server */
	if( stream->flags & STREAM_NFLAG_ISSERVER )
		status = writeResponseHeader( stream, length );
	else
		{
		assert( strlen( stream->contentType ) );
		assert( stream->host != NULL );

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

	/* Send the payload data to the client/server */
	return( stream->bufferedTransportWriteFunction( stream, buffer, length,
													TRANSPORT_FLAG_FLUSH ) );
	}

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

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

⌨️ 快捷键说明

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