📄 stream.c
字号:
/****************************************************************************
* *
* 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 + -