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

📄 stream.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*							Stream I/O Functions							*
*						Copyright Peter Gutmann 1993-2003					*
*																			*
****************************************************************************/

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
  #include "stream.h"
#elif defined( INC_CHILD )
  #include "stream.h"
#else
  #include "io/stream.h"
#endif /* Compiler-specific includes */

/* Prototypes for functions in str_file.c */

int fileRead( STREAM *stream, void *buffer, const int length );
int fileWrite( STREAM *stream, const void *buffer, const int length );
int fileFlush( STREAM *stream );
int fileSeek( STREAM *stream, const long position );

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* Exit after saving a detailed error message.  This is used by the stream
   transport-layer code to provide more information to the caller than a
   basic error code */

int retExtStreamFn( STREAM *stream, const int status, const char *format, ... )
	{
#ifdef USE_TCP
	va_list argPtr;

	va_start( argPtr, format );
	vsnprintf( stream->errorMessage, MAX_ERRMSG_SIZE, format, argPtr );
	va_end( argPtr );
#endif /* USE_TCP */
	stream->status = status;
	assert( !cryptArgError( status ) );	/* Catch leaks */
	return( cryptArgError( status ) ? CRYPT_ERROR_FAILED : status );
	}

/* Refill a stream buffer from backing storage */

static int refillStream( STREAM *stream )
	{
	int status;

	assert( stream->type == STREAM_TYPE_FILE );

	/* If we've reached EOF we can't refill it */
	if( stream->flags & STREAM_FFLAG_EOF )
		{
		/* If partial reads are allowed, return an indication of how much 
		   data we got.  This only works once, after this the persistent 
		   error state will return an underflow error before we get to this
		   point */
		stream->status = CRYPT_ERROR_UNDERFLOW;
		return( ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
				OK_SPECIAL : CRYPT_ERROR_UNDERFLOW );
		}

	/* If we've moved to a different place in the file, get new data into 
	   the buffer */
	if( ( stream->flags & STREAM_FFLAG_POSCHANGED ) && \
		!( stream->flags & STREAM_FFLAG_POSCHANGED_NOSKIP ) )
		{
		status = fileSeek( stream, stream->bufCount * stream->bufSize );
		if( cryptStatusError( status ) )
			{
			stream->status = status;
			return( status );
			}
		}

	/* Try and read more data into the stream buffer */
	status = fileRead( stream, stream->buffer, stream->bufSize );
	if( cryptStatusError( status ) )
		{
		stream->status = status;
		return( status );
		}
	if( status < stream->bufSize )
		{
		/* If we got less than we asked for, remember that we're at the end
		   of the file */
		stream->flags |= STREAM_FFLAG_EOF;
		if( status == 0 )
			{
			/* We ran out of input on an exact buffer boundary.  If partial 
			   reads are allowed, return an indication of how much data we 
			   got.  This only works once, after this the persistent error 
			   state will return an underflow error before we get to this 
			   point */
			stream->status = CRYPT_ERROR_UNDERFLOW;
			return( ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
					OK_SPECIAL : CRYPT_ERROR_UNDERFLOW );
			}
		}

	/* We've refilled the stream buffer from the file, remember the 
	   details */
	if( !( stream->flags & STREAM_FFLAG_POSCHANGED ) )
		{
		stream->bufCount++;
		stream->bufPos = 0;
		}
	stream->bufEnd = status;
	stream->flags &= ~( STREAM_FFLAG_POSCHANGED | \
						STREAM_FFLAG_POSCHANGED_NOSKIP );

	return( CRYPT_OK );
	}

/* Empty a stream buffer to backing storage */

static int emptyStream( STREAM *stream, const BOOLEAN forcedFlush )
	{
	int status = CRYPT_OK;

	assert( stream->type == STREAM_TYPE_FILE );

	/* If the stream position has been changed, this can only have been from 
	   a rewind of the stream, in which case we move back to the start of 
	   the file */
	if( stream->flags & STREAM_FFLAG_POSCHANGED )
		{
		status = fileSeek( stream, 0 );
		if( cryptStatusError( status ) )
			{
			stream->status = status;
			return( status );
			}
		}

	/* Try and write the data to the stream's backing storage */
	status = fileWrite( stream, stream->buffer, stream->bufPos );
	if( cryptStatusError( status ) )
		{
		stream->status = status;
		return( status );
		}

	/* Reset the position-changed flag and, if we've written another buffer 
	   full of data, remember the details.  If it's a forced flush we leave
	   everything as is, to remember the last write position in the file */
	stream->flags &= ~STREAM_FFLAG_POSCHANGED;
	if( !forcedFlush )
		{
		stream->bufCount++;
		stream->bufPos = 0;
		}

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Read/Write Functions							*
*																			*
****************************************************************************/

/* Read data from a stream */

int sgetc( STREAM *stream )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( stream->type == STREAM_TYPE_MEMORY || \
			stream->type == STREAM_TYPE_FILE );
	assert( stream->bufPos >= 0 && stream->bufPos <= stream->bufEnd );
	assert( isReadPtr( stream->buffer, stream->bufSize ) );

	/* Check that the input parameters are in order */
	if( !isWritePtr( stream, sizeof( STREAM ) ) )
		{
		assert( NOTREACHED );
		return( CRYPT_ERROR_READ );
		}
	if( stream->bufPos < 0 || stream->bufPos > stream->bufEnd )
		{
		assert( NOTREACHED );
		return( sSetError( stream, CRYPT_ERROR_READ ) );
		}

	/* If there's a problem with the stream, don't try to do anything */
	if( cryptStatusError( stream->status ) )
		return( stream->status );

	switch( stream->type )
		{
		case  STREAM_TYPE_MEMORY:
			assert( !( stream->flags & ~STREAM_FLAG_MASK ) );

			/* Read the data from the stream buffer */
			if( stream->bufPos >= stream->bufEnd )
				{
				stream->status = CRYPT_ERROR_UNDERFLOW;
				return( CRYPT_ERROR_UNDERFLOW );
				}
			return( stream->buffer[ stream->bufPos++ ] );

		case STREAM_TYPE_FILE:
			assert( !( stream->flags & ~STREAM_FFLAG_MASK ) );

			/* Read the data from the file */
			if( stream->bufPos >= stream->bufEnd || \
				( stream->flags & STREAM_FFLAG_POSCHANGED ) )
				{
				int status = refillStream( stream );
				if( cryptStatusError( status ) )
					return( ( status == OK_SPECIAL ) ? 0 : status );
				}
			return( stream->buffer[ stream->bufPos++ ] );
		}

	assert( NOTREACHED );
	return( CRYPT_ERROR_READ );		/* Get rid of compiler warning */
	}

int sread( STREAM *stream, void *buffer, const int length )
	{
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( stream->type == STREAM_TYPE_MEMORY || \
			stream->type == STREAM_TYPE_FILE || \
			stream->type == STREAM_TYPE_NETWORK );
	assert( stream->bufPos >= 0 && stream->bufPos <= stream->bufEnd );
	assert( stream->type == STREAM_TYPE_NETWORK || \
			isReadPtr( stream->buffer, stream->bufSize ) );
	assert( isWritePtr( buffer, length ) );
	assert( length > 0 );

	/* Check that the input parameters are in order */
	if( !isWritePtr( stream, sizeof( STREAM ) ) )
		{
		assert( NOTREACHED );
		return( CRYPT_ERROR_READ );
		}
	if( stream->bufPos < 0 || stream->bufPos > stream->bufEnd || \
		!isWritePtr( buffer, length ) )
		{
		assert( NOTREACHED );
		return( sSetError( stream, CRYPT_ERROR_READ ) );
		}

	/* If there's a problem with the stream, don't try to do anything */
	if( cryptStatusError( stream->status ) )
		return( stream->status );

	switch( stream->type )
		{
		case  STREAM_TYPE_MEMORY:
			assert( !( stream->flags & ~STREAM_FLAG_MASK ) );

			/* Read the data from the stream buffer */
			if( stream->bufPos + length > stream->bufEnd )
				{
				memset( buffer, 0, length );	/* Clear the output buffer */
				stream->status = CRYPT_ERROR_UNDERFLOW;
				return( CRYPT_ERROR_UNDERFLOW );
				}
			memcpy( buffer, stream->buffer + stream->bufPos, length );
			stream->bufPos += length;

			return( CRYPT_OK );

		case STREAM_TYPE_FILE:
			{
			BYTE *bufPtr = buffer;
			int dataLength = length, bytesCopied = 0;

			assert( !( stream->flags & ~STREAM_FFLAG_MASK ) );

			/* Read the data from the file */
			while( dataLength > 0 )
				{
				int bytesToCopy;

				/* If the stream buffer is empty, try and refill it */
				if( stream->bufPos >= stream->bufEnd || \
					( stream->flags & STREAM_FFLAG_POSCHANGED ) )
					{
					status = refillStream( stream );
					if( cryptStatusError( status ) )
						return( ( status == OK_SPECIAL ) ? \
								bytesCopied : status );
					}

				/* Copy as much data as we can out of the stream buffer */
				bytesToCopy = min( dataLength, \
								   stream->bufEnd - stream->bufPos );
				memcpy( bufPtr, stream->buffer + stream->bufPos, 
						bytesToCopy );
				stream->bufPos += bytesToCopy;
				bufPtr += bytesToCopy;
				bytesCopied += bytesToCopy;
				dataLength -= bytesToCopy;
				}

			/* Usually reads are atomic so we just return an all-OK 
			   indicator, however if we're performing partial reads we need
			   to return an exact byte count */
			return( ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
					bytesCopied : CRYPT_OK );
			}

#ifdef USE_TCP
		case STREAM_TYPE_NETWORK:
			assert( !( stream->flags & ~STREAM_NFLAG_MASK ) );
			assert( stream->readFunction != NULL );
			assert( ( stream->flags & STREAM_NFLAG_ISSERVER ) || \
					stream->host != NULL || \
					stream->netSocket != CRYPT_ERROR );
			assert( stream->timeout >= 0 && stream->timeout <= 300 );

			/* Read the data from the network.  Reads are normally atomic, 
			   but when doing bulk data transfers can be restarted after
			   a timeout */
			status = stream->readFunction( stream, buffer, length );
			if( cryptStatusError( status ) )
				{
				if( status != CRYPT_ERROR_COMPLETE )
					return( status );

				/* If we get a CRYPT_ERROR_COMPLETE status this means that
				   the other side has closed the connection.  This status is 
				   returned when there are intermediate protocol layers such 
				   as HTTP or tunnelling over a cryptlib session involved.
				   When this occurs we update the stream state and map the 
				   status to a standard read error.  The exact code to 
				   return here is a bit uncertain, it isn't specifically a 
				   read error because either the other side is allowed to 
				   close the connection after it's said its bit (and so it's 
				   not a read error), or it has to perform a 
				   cryptographically protected close (in which case any 
				   non-OK status indicates a problem).  The most sensible 
				   status is probably a read error */
				sioctl( stream, STREAM_IOCTL_CONNSTATE, NULL, FALSE );
				return( CRYPT_ERROR_READ );
				}
			if( status < length && \
				!( stream->flags & ( STREAM_FLAG_PARTIALREAD | \
									 STREAM_NFLAG_ENCAPS ) ) )
				{
				/* If we didn't read all of the data and partial reads 
				   aren't allowed, report a read timeout */
				retExtStream( stream, CRYPT_ERROR_TIMEOUT,
							  "Read timed out with %d of %d bytes read",
							  status, length );
				}
			return( status );
#endif /* USE_TCP */
		}

	assert( NOTREACHED );
	return( CRYPT_ERROR_READ );		/* Get rid of compiler warning */
	}

/* Write data to a stream */

int sputc( STREAM *stream, const int ch )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( stream->type == STREAM_TYPE_NULL || \
			stream->type == STREAM_TYPE_MEMORY || \
			stream->type == STREAM_TYPE_FILE );
	assert( stream->type == STREAM_TYPE_NULL || \
			isWritePtr( stream->buffer, stream->bufSize ) );
	assert( stream->type == STREAM_TYPE_NULL || \
			stream->bufPos >= 0 && stream->bufPos <= stream->bufSize );
	assert( !( stream->flags & STREAM_FLAG_READONLY ) );

	/* Check that the input parameters are in order */
	if( !isWritePtr( stream, sizeof( STREAM ) ) )
		{

⌨️ 快捷键说明

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