📄 stream.c
字号:
if( localLength > 0 )
{
strncpy( stream->errorMessage, bufPtr,
min( localLength, MAX_ERRMSG_SIZE - 1 ) );
stream->errorMessage[ MAX_ERRMSG_SIZE - 1 ] = '\0';
}
else
strcpy( stream->errorMessage,
"Unknown CMP transport-level error encountered" );
/* The appropriate status values to return for a problem at this
level are pretty unclear, the most appropriate ones appear to be
a read error if there's a problem with the server (exactly what
the problem is is never specified in the error code) and a generic
bad data for anything else */
return( ( ( stream->errorCode & 0x0F00 ) == 0x0300 ) ? \
CRYPT_ERROR_READ : CRYPT_ERROR_BADDATA );
}
return( CRYPT_ERROR_BADDATA );
}
/* Handle any special handshaking and protocol headers which may be present
with some network stream types */
static int readProtocolHeader( STREAM *stream, void **bufPtr, int *length,
const int maxLength, const BOOLEAN isServer )
{
int localLength = maxLength, status;
/* Clear return value */
*length = CRYPT_ERROR;
/* If it's server tunnelling data over HTTP, read the request header from
the client */
if( isServer && stream->protocol == STREAM_PROTOCOL_HTTP )
{
char lineBuffer[ HTTP_LINEBUF_SIZE ];
/* Read the POST header and check for "POST x HTTP/1.x" */
status = readLine( stream, lineBuffer );
if( !cryptStatusError( status ) )
{
if( status < 15 || strncmp( lineBuffer, "POST ", 5 ) )
status = CRYPT_ERROR_BADDATA;
else
{
int i = 4;
/* Skip ' '* * ' '* */
while( lineBuffer[ i ] && lineBuffer[ i ] == ' ' )
i++;
while( lineBuffer[ i ] && lineBuffer[ i ] != ' ' )
i++;
while( lineBuffer[ i ] && lineBuffer[ i ] == ' ' )
i++;
if( i > HTTP_LINEBUF_SIZE - 9 || \
strncmp( lineBuffer + i, "HTTP/1.", 7 ) )
status = CRYPT_ERROR_READ;
}
}
if( cryptStatusError( status ) )
return( status );
/* We've got a valid response, look for a length indicator and skip
any other header lines */
do
{
status = readLine( stream, lineBuffer );
if( status > 14 && !strncmp( lineBuffer, "Content-Length", 14 ) )
{
const char *lengthPos = strchr( lineBuffer, ' ' ) + 1;
if( lengthPos == NULL )
status = CRYPT_ERROR_BADDATA;
else
{
/* Make sure the length is sensible. ~64 bytes is the
minimum-size object which can be returned from any
HTTP-based message which is exchanged by cryptlib,
this being a TSP request */
localLength = atoi( lengthPos );
if( localLength < 64 )
status = CRYPT_ERROR_BADDATA;
else
if( localLength > maxLength )
status = CRYPT_ERROR_OVERFLOW;
}
}
}
while( status > 0 );
if( cryptStatusError( status ) )
return( status );
*length = localLength;
return( CRYPT_OK );
}
/* If it's a fully stateless HTTP read, we need to connect to the server
and send the fetch request before we can read anything */
if( stream->protocol == STREAM_PROTOCOL_HTTP )
{
char headerBuffer[ MAX_URL_SIZE + 128 ];
assert( stream->netSocket == CRYPT_ERROR );
assert( stream->protocolState == STREAM_PROTOCOLSTATE_NONE );
assert( !*stream->contentType );
/* Open the connection to the remote host */
status = connectStream( stream );
if( cryptStatusError( status ) )
return( status );
/* Send the HTTP GET header and drop through to reading the
response */
strcpy( headerBuffer, "GET " );
strcat( headerBuffer, stream->path );
if( stream->query != NULL )
{
strcat( headerBuffer, "?" );
strcat( headerBuffer, stream->query );
}
strcat( headerBuffer, " HTTP/1.0\r\n\r\n" );
status = writeSocket( stream, headerBuffer, strlen( headerBuffer ) );
if( cryptStatusError( status ) )
return( status );
stream->protocolState = STREAM_PROTOCOLSTATE_FETCHSENT;
}
/* If it's an HTTP read, read the out-of-band header data from the
server */
if( stream->protocol == STREAM_PROTOCOL_HTTP || \
stream->protocol == STREAM_PROTOCOL_HTTP_TRANSACTION )
{
char lineBuffer[ HTTP_LINEBUF_SIZE ];
assert( stream->protocolState == STREAM_PROTOCOLSTATE_FETCHSENT );
/* Read the response header and check for "HTTP/1.x 200" */
status = readLine( stream, lineBuffer );
if( !cryptStatusError( status ) )
{
if( strncmp( lineBuffer, "HTTP/1.", 7 ) )
status = CRYPT_ERROR_BADDATA;
else
if( strncmp( lineBuffer + 9, "200", 3 ) )
status = CRYPT_ERROR_READ;
}
if( cryptStatusError( status ) )
return( status );
/* We've got a valid response, look for a length indicator and skip
any other header lines */
do
{
status = readLine( stream, lineBuffer );
if( cryptStatusError( status ) )
return( status );
if( status > 14 && !strncmp( lineBuffer, "Content-Length", 14 ) )
{
const char *lengthPos = strchr( lineBuffer, ' ' ) + 1;
/* Make sure the length is sensible. 5 bytes is the minimum-
size object which can be returned from any HTTP-based
message which is exchanged by cryptlib, this being an
OCSP response containing a single-byte status value, ie
SEQUENCE { ENUM x } */
if( lengthPos == NULL )
return( CRYPT_ERROR_BADDATA );
localLength = atoi( lengthPos );
if( localLength < 5 )
return( CRYPT_ERROR_BADDATA );
/* If there's a buffer-adjust callback present, try and
increase the buffer size */
if( stream->callbackFunction != NULL )
{
int callbackStatus;
assert( stream->callbackParams != NULL );
callbackStatus = \
stream->callbackFunction( stream->callbackParams,
bufPtr, localLength );
if( cryptStatusError( callbackStatus ) )
return( callbackStatus );
}
else
if( localLength > maxLength )
return( CRYPT_ERROR_OVERFLOW );
}
}
while( status > 0 );
*length = localLength;
stream->protocolState = STREAM_PROTOCOLSTATE_DATAFETCHED;
return( CRYPT_OK );
}
/* If it's a packet-based protocol, read the packet length from the
stream */
if( stream->protocol == STREAM_PROTOCOL_CMP )
return( readCmpHeader( stream, length, maxLength ) );
*length = maxLength;
return( CRYPT_OK );
}
#endif /* NET_TCP */
/* OS-specific support routines */
#ifdef __MAC__
static void CStringToPString( const char *cstring, StringPtr pstring )
{
short len = min( strlen( cstring ), 255 );
memmove( pstring+1, cstring, len );
*pstring = len;
}
#endif /* __MAC__ */
/****************************************************************************
* *
* Generic Stream I/O Functions *
* *
****************************************************************************/
/* Read a byte from a stream */
int sgetc( STREAM *stream )
{
#if defined( __WIN32__ )
DWORD bytesRead;
BYTE ch;
#elif defined( __MAC__ )
long bytesRead = 1;
BYTE ch;
#else
int ch;
#endif /* OS-specific variable declarations */
assert( stream != NULL && \
( stream->type == STREAM_TYPE_MEMORY || \
stream->type == STREAM_TYPE_FILE ) );
/* If there's a problem with the stream, don't try to do anything */
if( stream->status != CRYPT_OK )
return( stream->status );
/* If we ungot a char, return this */
if( stream->ungetChar )
{
ch = stream->lastChar;
stream->ungetChar = FALSE;
return( ch );
}
/* If it's a memory stream, read the data from the buffer */
if( stream->type == STREAM_TYPE_MEMORY )
{
if( stream->bufSize != STREAMSIZE_UNKNOWN && \
stream->bufPos >= stream->bufEnd )
{
stream->status = CRYPT_ERROR_UNDERFLOW;
return( CRYPT_ERROR_UNDERFLOW );
}
stream->lastChar = stream->buffer[ stream->bufPos++ ];
return( stream->lastChar );
}
#ifndef NO_STDIO
/* It's a file stream, read the data from the file */
#if defined( __WIN32__ )
if( !ReadFile( stream->hFile, &ch, 1, &bytesRead, NULL ) || !bytesRead )
#elif defined( __MAC__ )
if( FSRead( stream->refNum, &bytesRead, &ch) != noErr || !bytesRead )
#else
if( ( ch = getc( stream->filePtr ) ) == EOF )
#endif /* __WIN32__ */
{
stream->status = CRYPT_ERROR_UNDERFLOW;
return( CRYPT_ERROR_UNDERFLOW );
}
#else
assert( NOTREACHED );
#endif /* NO_STDIO */
return( stream->lastChar = ch );
}
/* Write a byte to a stream */
int sputc( STREAM *stream, int data )
{
#if defined( __WIN32__ )
DWORD bytesWritten;
const BYTE byteData = data;
#elif defined( __MAC__ )
long bytesWritten = 1;
const BYTE byteData = data;
#endif /* Win32 || Macintosh */
assert( stream != NULL );
assert( stream->type == STREAM_TYPE_NULL || \
stream->type == STREAM_TYPE_MEMORY || \
stream->type == STREAM_TYPE_FILE );
assert( !( 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( stream->status != CRYPT_OK )
return( stream->status );
/* If it's a null stream, just record the write and return */
if( stream->type == STREAM_TYPE_NULL )
{
stream->bufPos++;
return( CRYPT_OK );
}
/* If we ungot a char, move back one entry in the buffer */
if( stream->ungetChar && stream->bufPos )
{
stream->bufPos--;
stream->ungetChar = FALSE;
}
/* If it's a memory stream, deposit the data in the buffer */
if( stream->type == STREAM_TYPE_MEMORY )
{
if( stream->bufSize != STREAMSIZE_UNKNOWN && \
stream->bufPos >= stream->bufSize )
{
#ifdef NO_STDIO
/* If it's a non-sensitive pseudo-I/O stream, expand the buffer */
if( stream->isIOStream && !stream->isSensitive )
{
const int status = expandBuffer( stream, 1 );
if( cryptStatusError( status ) )
return( status );
}
else
#endif /* NO_STDIO */
{
stream->status = CRYPT_ERROR_OVERFLOW;
return( CRYPT_ERROR_OVERFLOW );
}
}
stream->buffer[ stream->bufPos++ ] = data;
if( stream->bufEnd < stream->bufPos )
/* Move up the end-of-data pointer if necessary */
stream->bufEnd = stream->bufPos;
stream->flags |= STREAM_FLAG_DIRTY;
return( CRYPT_OK );
}
#ifndef NO_STDIO
/* It's a file stream, write the data to the file */
#if defined( __WIN32__ )
if( !WriteFile( stream->hFile, &byteData, 1, &bytesWritten, NULL ) || \
!bytesWritten )
#elif defined( __MAC__ )
if( FSWrite( stream->refNum, &bytesWritten, &byteData ) != noErr || \
!bytesWritten )
#else
if( putc( data, stream->filePtr ) == EOF )
#endif /* __WIN32__ */
{
stream->status = CRYPT_ERROR_WRITE;
return( CRYPT_ERROR_WRITE );
}
stream->flags |= STREAM_FLAG_DIRTY;
#else
assert( NOTREACHED );
#endif /* NO_STDIO */
return( CRYPT_OK );
}
/* Unget a byte from a stream */
int sungetc( STREAM *stream )
{
assert( stream != NULL && \
( stream->type == STREAM_TYPE_NULL || \
stream->type == STREAM_TYPE_MEMORY || \
stream->type == STREAM_TYPE_FILE ) );
/* If the stream is empty, calling this function resets the stream
status to nonempty (since we can't read past EOF, ungetting even one
char will reset the stream status). If the stream isn't empty, we
set a flag to indicate that we should return the last character read
in the next read call */
if( stream->status == CRYPT_ERROR_UNDERFLOW )
stream->status = CRYPT_OK;
else
stream->ungetChar = TRUE;
return( CRYPT_OK );
}
/* Read a block of data from a stream. If not enough data is available it
will fail with CRYPT_ERROR_UNDERFLOW rather than trying to read as much
as it can, which mirrors the behaviour of most read()/fread()
implementations, however with some higher-level network protocols we don't
know the data length in advance so we specify the maximum length which
we're prepared to accept and let sread() sort out the details */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -