📄 stream.c
字号:
default:
retIntError_Stream( stream );
}
ENSURES_S( sanityCheck( stream ) );
return( ch );
}
RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int sread( INOUT STREAM *stream,
OUT_BUFFER_FIXED( length ) void *buffer,
IN_LENGTH const int length )
{
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( stream->type == STREAM_TYPE_NETWORK || \
isReadPtr( stream->buffer, stream->bufSize ) );
assert( isWritePtr( buffer, length ) );
/* Check that the input parameters are in order */
if( !isWritePtr( stream, sizeof( STREAM ) ) )
retIntError();
if( !isWritePtr( buffer, length ) )
retIntError_Stream( stream );
REQUIRES_S( sanityCheck( stream ) );
REQUIRES_S( stream->type == STREAM_TYPE_MEMORY || \
stream->type == STREAM_TYPE_FILE || \
stream->type == STREAM_TYPE_NETWORK );
REQUIRES_S( length > 0 && length < MAX_INTLENGTH );
/* 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:
{
int localLength = length;
#ifdef VIRTUAL_FILE_STREAM
/* If partial reads are allowed return whatever's left in the
stream buffer. This only occurs for virtual file streams
that have been translated into memory streams */
if( stream->flags & STREAM_FLAG_PARTIALREAD )
{
REQUIRES_S( sIsVirtualFileStream( stream ) );
localLength = stream->bufEnd - stream->bufPos;
}
#endif /* VIRTUAL_FILE_STREAM */
/* Read the data from the stream buffer */
if( stream->bufPos + localLength > stream->bufEnd )
{
memset( buffer, 0, min( 16, length ) ); /* Clear output buffer */
return( sSetError( stream, CRYPT_ERROR_UNDERFLOW ) );
}
memcpy( buffer, stream->buffer + stream->bufPos, localLength );
stream->bufPos += localLength;
/* 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 */
status = ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
localLength : CRYPT_OK;
break;
}
case STREAM_TYPE_FILE:
{
BYTE *bufPtr = buffer;
int dataLength, bytesCopied = 0, iterationCount;
REQUIRES_S( stream->flags & STREAM_FFLAG_BUFFERSET );
/* Read the data from the file */
for( dataLength = length, iterationCount = 0;
dataLength > 0 && iterationCount < FAILSAFE_ITERATIONS_LARGE;
iterationCount++ )
{
const int oldDataLength = dataLength;
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;
ENSURES_S( dataLength < oldDataLength );
}
ENSURES_S( iterationCount < FAILSAFE_ITERATIONS_LARGE );
/* 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 */
status = ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
bytesCopied : CRYPT_OK;
break;
}
#ifdef USE_TCP
case STREAM_TYPE_NETWORK:
{
NET_STREAM_INFO *netStream = \
( NET_STREAM_INFO * ) stream->netStreamInfo;
int bytesRead;
REQUIRES_S( netStream->protocol != STREAM_PROTOCOL_HTTP || \
( netStream->protocol == STREAM_PROTOCOL_HTTP && \
length == sizeof( HTTP_DATA_INFO ) ) );
/* Read the data from the network. Reads are normally atomic
but if the partial-write flag is set can be restarted after
a timeout */
status = netStream->readFunction( stream, buffer, length,
&bytesRead );
if( cryptStatusError( status ) )
{
/* If the lower-level code has indicated that the error
condition is fatal, make it persistent for the stream */
if( cryptStatusError( netStream->persistentStatus ) )
stream->status = netStream->persistentStatus;
/* If it's not a special-case CRYPT_ERROR_COMPLETE status,
exit. We don't make the error persistent since unlike
memory or file stream reads, most errors on network reads
are recoverable */
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( bytesRead < length && \
!( ( stream->flags & STREAM_FLAG_PARTIALREAD ) || \
( netStream->nFlags & STREAM_NFLAG_ENCAPS ) ) )
{
/* If we didn't read all of the data and partial reads
aren't allowed report a read timeout. The situation
for HTTP streams is a bit special because what we're
sending to the read function is an HTTP_DATA_INFO
structure so we have to extract the actual length
information from that */
if( netStream->protocol == STREAM_PROTOCOL_HTTP )
{
const HTTP_DATA_INFO *httpDataInfo = \
( HTTP_DATA_INFO * ) buffer;
retExt( CRYPT_ERROR_TIMEOUT,
( CRYPT_ERROR_TIMEOUT, NETSTREAM_ERRINFO,
"Read timed out with %d of %d bytes read",
httpDataInfo->bytesTransferred,
httpDataInfo->bytesAvail ) );
}
retExt( CRYPT_ERROR_TIMEOUT,
( CRYPT_ERROR_TIMEOUT, NETSTREAM_ERRINFO,
"Read timed out with %d of %d bytes read",
bytesRead, length ) );
}
/* This is an ugly case where we have to follow the Posix
semantics of returning a read-bytes count as the return
status rather than a by-reference parameter. If we didn't
do this then every trivial memory-stream read would need to
pass in a dummy parameter for the read-byte-count value just
to handle the one or two calls to a network stream read that
needs to return a length */
status = bytesRead;
break;
}
#endif /* USE_TCP */
default:
retIntError_Stream( stream );
}
ENSURES_S( sanityCheck( stream ) );
return( status );
}
/****************************************************************************
* *
* Stream Write Functions *
* *
****************************************************************************/
/* Write data to a stream */
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int sputc( INOUT STREAM *stream, IN_BYTE const int ch )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( stream->type == STREAM_TYPE_NULL || \
isWritePtr( stream->buffer, stream->bufSize ) );
/* Check that the input parameters are in order */
if( !isWritePtr( stream, sizeof( STREAM ) ) )
retIntError();
REQUIRES_S( sanityCheck( stream ) );
REQUIRES_S( stream->type == STREAM_TYPE_NULL || \
stream->type == STREAM_TYPE_MEMORY || \
stream->type == STREAM_TYPE_FILE );
REQUIRES_S( !( stream->flags & STREAM_FLAG_READONLY ) );
REQUIRES( ch >= 0 && ch <= 0xFF );
/* If there's a problem with the stream don't try to do anything until
the error is cleared */
if( cryptStatusError( stream->status ) )
return( stream->status );
switch( stream->type )
{
case STREAM_TYPE_NULL:
/* It's a null stream, just record the write and return */
stream->bufPos++;
if( stream->bufEnd < stream->bufPos )
stream->bufEnd = stream->bufPos;
break;
case STREAM_TYPE_MEMORY:
/* Write the data to the stream buffer */
if( stream->bufPos >= stream->bufSize )
{
#ifdef VIRTUAL_FILE_STREAM
if( sIsVirtualFileStream( stream ) )
{
int status;
status = expandVirtualFileStream( stream, 1 );
if( cryptStatusError( status ) )
return( status );
}
else
#endif /* VIRTUAL_FILE_STREAM */
return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );
}
stream->buffer[ stream->bufPos++ ] = ch;
if( stream->bufEnd < stream->bufPos )
stream->bufEnd = stream->bufPos;
#ifdef VIRTUAL_FILE_STREAM
if( sIsVirtualFileStream( stream ) )
{
/* This is a memory stream emulating a file stream, set the
dirty bit */
stream->flags |= STREAM_FLAG_DIRTY;
}
#endif /* VIRTUAL_FILE_STREAM */
break;
case STREAM_TYPE_FILE:
REQUIRES_S( stream->flags & STREAM_FFLAG_BUFFERSET );
/* Write the data to the file */
if( stream->bufPos >= stream->bufSize )
{
int status;
status = emptyStream( stream, FALSE );
if( cryptStatusError( status ) )
return( status );
}
stream->buffer[ stream->bufPos++ ] = ch;
stream->flags |= STREAM_FLAG_DIRTY;
break;
default:
retIntError_Stream( stream );
}
ENSURES_S( sanityCheck( stream ) );
return( CRYPT_OK );
}
RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int swrite( INOUT STREAM *stream,
IN_BUFFER( length ) const void *buffer,
IN_LENGTH const int length )
{
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( stream->type == STREAM_TYPE_NULL || \
stream->type == STREAM_TYPE_NETWORK || \
isWritePtr( stream->buffer, stream->bufSize ) );
assert( isReadPtr( buffer, length ) );
/* Check that the input parameters are in order */
if( !isWritePtr( stream, sizeof( STREAM ) ) )
retIntError();
if( !isReadPtr( buffer, length ) )
retIntError_Stream( stream );
REQUIRES_S( sanityCheck( stream ) );
REQUIRES_S( stream->type == STREAM_TYPE_NULL || \
stream->type == STREAM_TYPE_MEMORY || \
stream->type == STREAM_TYPE_FILE || \
stream->type == STREAM_TYPE_NETWORK );
REQUIRES_S( length > 0 && length < MAX_INTLENGTH );
REQUIRES_S( !( stream->flags & STREAM_FLAG_READONLY ) );
/* If there's a problem with the stream don't try to do anything until
the error is cleared */
if( cryptStatusError( stream->status ) )
return( stream->status );
switch( stream->type )
{
case STREAM_TYPE_NULL:
/* It's a null stream, just record the write and return */
stream->bufPos += length;
if( stream->bufEnd < stream->bufPos )
stream->bufEnd = stream->bufPos;
status = CRYPT_OK;
break;
case STREAM_TYPE_MEMORY:
/* Write the data to the stream buffer */
if( stream->bufPos + length > stream->bufSize )
{
#ifdef VIRTUAL_FILE_STREAM
if( sIsVirtualFileStream( stream ) )
{
status = expandVirtualFileStream( stream, length );
if( cryptStatusError( status ) )
return( status );
}
else
#endif /* VIRTUAL_FILE_STREAM */
return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );
}
memcpy( stream->buffer + stream->bufPos, buffer, length );
stream->bufPos += length;
if( stream->bufEnd < stream->bufPos )
stream->bufEnd = stream->bufPos;
#ifdef VIRTUAL_FILE_STREAM
if( sIsVirtualFileStream( stream ) )
{
/* This is a memory stream emulating a file stream, set the
dirty bit */
stream->flags |= STREAM_FLAG_DIRTY;
}
#endif /* VIRTUAL_FILE_STREAM */
status = CRYPT_OK;
break;
case STREAM_TYPE_FILE:
{
const BYTE *bufPtr = buffer;
int dataLength, iterationCount;
REQUIRES_S( stream->flags & STREAM_FFLAG_BUFFERSET );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -