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

📄 http.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 4 页
字号:
					{
					lineBuffer[ CRYPT_MAX_TEXTSIZE ] = '\0';
					retExtStream( stream, CRYPT_ERROR_BADDATA,
								  "Invalid HTTP transfer encoding method "
								  "'%s', expected 'Chunked'", 
								  sanitiseString( lineBuffer ) );
					}

				/* If it's a chunked encoding, the length is part of the 
				   data and must be read later */
				if( seenLength )
					retExtStream( stream, CRYPT_ERROR_BADDATA,
								  "Duplicate 'Content-Length:' header line" );
				*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', expected 'Identity'",
								  sanitiseString( 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 that 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', expected 'Identity' or "
								  "'Binary'", sanitiseString( lineBuffer ) );
					}
				break;

			case HTTP_HEADER_TRAILER:
				/* The body is followed by trailer lines, used with chunked
				   encodings 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 this is an tunnel being opened via an HTTP proxy, we're done */
	if( !( stream->flags & STREAM_NFLAG_ISSERVER ) && \
		( stream->flags & STREAM_NFLAG_HTTPTUNNEL ) )
		return( CRYPT_OK );

	/* 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 an error or 100 
	   Continue response), all that we're interested in is draining the 
	   input, so we don't check any further */
	if( *flags & HTTP_FLAG_NOOP )
		return( CRYPT_OK );

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

	/* If it's an idempotent read there's no length, just a GET request, so
	   we can exit now */
	if( stream->flags & STREAM_NFLAG_IDEMPOTENT )
		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 either 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, need %d bytes but "
					  "only got %d", minLength, localLength );
	if( !expandBuffer && ( localLength > maxLength ) )
		{
		if( httpErrorStatus != NULL )
			*httpErrorStatus = 413;
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Excessive HTTP content data, got %d bytes when "
					  "maximum was %d", localLength, maxLength );
		}
	if( contentLength != NULL )
		*contentLength = localLength;

	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 + 8 ];
	const int transportFlag = ( length > 0 ) ? TRANSPORT_FLAG_NONE : \
											   TRANSPORT_FLAG_FLUSH;
	const int hostLen = strlen( stream->host );
	int headerLength;

	sMemOpen( &headerStream, headerBuffer, HTTP_LINEBUF_SIZE );
	if( stream->flags & STREAM_NFLAG_HTTPTUNNEL )
		swrite( &headerStream, "CONNECT ", 8 );
	else
		if( length > 0 )
			swrite( &headerStream, "POST ", 5 );
		else
			swrite( &headerStream, "GET ", 4 );
	if( ( stream->flags & STREAM_NFLAG_HTTPPROXY ) || \
		( stream->flags & STREAM_NFLAG_HTTPTUNNEL ) )
		{
		/* If we're going through an HTTP proxy/tunnel, send an absolute URL 
		   rather than just the relative location */
		if( stream->flags & STREAM_NFLAG_HTTPPROXY )
			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->flags & STREAM_NFLAG_HTTPTUNNEL ) )
		{
		if( stream->path != NULL && *stream->path != '\0' )
			swrite( &headerStream, stream->path, strlen( stream->path ) );
		else
			sputc( &headerStream, '/' );
		}
	if( stream->query != NULL && *stream->query != '\0' )
		{
		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 );
	return( sendHTTPData( 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 *bufPtr;
	const char *reqName = ( stream->flags & STREAM_NFLAG_IDEMPOTENT ) ? \
						  "GET " : "POST ";
	const int reqNameLen = ( stream->flags & STREAM_NFLAG_IDEMPOTENT ) ? \
						   4 : 5;
	int bufMaxLen = maxLength, idempotentReadLength = 0, length;
	int httpStatus, status;

	assert( stream->flags & STREAM_NFLAG_ISSERVER );

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

	/* Read the header and check for "POST/GET 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 = length = 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, reqName, reqNameLen ) )
		{
		char reqNameBuffer[ 16 ];

		strcpy( reqNameBuffer, reqName );
		reqNameBuffer[ reqNameLen - 1 ] = '\0';	/* Strip trailing space */
		sendHTTPError( stream, buffer, 501 );
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Invalid HTTP request, expected '%s'", reqName );
		}
	bufPtr = buffer + reqNameLen;

	/* Process the ' '* * ' '* and check for the HTTP ID */
	if( ( bufPtr = skipWhitespace( bufPtr ) ) == NULL )
		{
		sendHTTPError( stream, buffer, 400 );
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Missing HTTP request URI" );
		}
	if( stream->flags & STREAM_NFLAG_IDEMPOTENT )
		{
		/* If it's an indempotent read the client is sending a GET rather
		   than submitting a POST, process the request details */
		status = parseURI( buffer, &idempotentReadLength, bufPtr, 
						   length - ( bufPtr - buffer ) );
		if( cryptStatusError( status ) || status < 10 )
			{
			sendHTTPError( stream, buffer, 400 );
			retExtStream( stream, CRYPT_ERROR_BADDATA,
						  "Invalid HTTP GET request URI" );
			}
		bufPtr = buffer + status;

		/* At this point part of the read buffer contains the data to be
		   returned to the caller, with the remainder of the buffer 
		   available for processing additional header lines.  To handle this 
		   we adjust the maximum buffer size to accomodate the data already 
		   in the buffer */
		bufMaxLen = maxLength - idempotentReadLength;
		}
	else
		/* For non-idempotent queries 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 */
		while( *bufPtr && *bufPtr != ' ' )
			bufPtr++;
	if( ( bufPtr = skipWhitespace( bufPtr ) ) == NULL )
		{
		sendHTTPError( stream, buffer, 400 );
		retExtStream( stream, CRYPT_ERROR_BADDATA,
					  "Missing HTTP request ID/version" );
		}
	status = checkHTTPID( stream, bufPtr, length - ( bufPtr - buffer ) );
	if( cryptStatusError( status ) )
		{
		sendHTTPError( stream, buffer, 505 );
		retExtStream( stream, status, "Invalid HTTP request 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 + idempotentReadLength, 
							  contentLength, &httpStatus, flags, 32, 
							  bufMaxLen, 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 );

	/* If it's an idempotent read, the content length is the length of the 
	   request data and not the body, since there isn't one */
	if( stream->flags & STREAM_NFLAG_IDEMPOTENT )
		*contentLength = idempotentReadLength;

⌨️ 快捷键说明

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