📄 cryptses.c
字号:
no-op packet was read and the caller should try again without
changing the read timeout value.
4. A byte count if a complete packet was read and processed */
status = tryRead( sessionInfoPtr, &readInfo );
if( cryptStatusError( status ) && status != OK_SPECIAL )
{
/* If there's an error reading data, only return an error status
if we haven't already returned existing/earlier data. This
ensures that the caller can drain out any remaining data from
the session buffer before they start getting error returns */
if( bytesCopied <= 0 )
{
bytesCopied = status;
if( readInfo == READINFO_FATAL )
sessionInfoPtr->readErrorState = status;
}
else
/* If it's a fatal error, save the pending error state for
later while returning the read byte count to the caller.
Note that this results in non-fatal errors being quietly
dropped if data is otherwise available, the alternative
would be to save it as a pending (specially-marked) non-
fatal error, however since this error type by definition
can be resumed it may already have resolved itself by the
next time we're called, so this is safe to do */
if( readInfo == READINFO_FATAL )
sessionInfoPtr->pendingErrorState = status;
break;
}
if( status == 0 )
/* We got nothing, exit */
break;
if( status == OK_SPECIAL )
{
/* If we read a partial packet and there's room for the rest of
the packet in the buffer, set a minimum timeout to try and
get the rest of the packet. This is safe because tryRead()
could have behaved in only one of two ways:
1. Blocking read, in which case we waited for the full
timeout period anyway and a small additional timeout
won't be noticed.
2. Nonblocking read, in which case waiting for a nonzero
time could potentially have retrieved more data */
assert( readInfo == READINFO_PARTIAL || \
readInfo == READINFO_NOOP );
if( readInfo == READINFO_PARTIAL && \
sessionInfoPtr->pendingPacketRemaining <= \
sessionInfoPtr->receiveBufSize - sessionInfoPtr->receiveBufEnd )
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_TIMEOUT, NULL, 1 );
}
else
{
/* Make the stream nonblocking if it was blocking before. This is
necessary to avoid having the stream always block for the set
timeout value on the last read */
assert( status > 0 );
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_TIMEOUT, NULL, 0 );
}
assert( sessionInfoPtr->receiveBufEnd <= \
sessionInfoPtr->receiveBufSize );
assert( sessionInfoPtr->receiveBufPos <= \
sessionInfoPtr->receiveBufEnd );
}
sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_TIMEOUT, NULL,
savedTimeout );
return( bytesCopied );
}
/* Send data to the remote system. Session buffer management is handled as
follows: The startOfs index points to the start of the payload space in
the buffer (everything before this is header data). The maxPos index
points to the end of the payload space relative to the start of the buffer.
This is needed for cases where the packet size is smaller than the buffer
size:
<- hdr->|<-- payload -->|
+-------+---------------+---+
| |///////////////| |
+-------+---------------+---+
^ ^
| |
startOfs maxPos
The bPos index moves from startsOfs to maxPos, after which the data is
flushed and the bPos index reset */
static int putData( SESSION_INFO *sessionInfoPtr, const void *data,
const int length )
{
const PROTOCOL_INFO *protocolInfoPtr = sessionInfoPtr->protocolInfo;
BYTE *dataPtr = ( BYTE * ) data;
int dataLength = length;
assert( sessionInfoPtr->sendBufPos >= sessionInfoPtr->sendBufStartOfs && \
sessionInfoPtr->sendBufPos <= protocolInfoPtr->sendBufMaxPos );
/* If it's a flush, send the data through to the server and restart at
the start of the buffer payload space */
if( dataLength <= 0 )
{
int status;
if( sessionInfoPtr->sendBufPos <= sessionInfoPtr->sendBufStartOfs )
return( CRYPT_OK ); /* There's no data to flush, exit */
status = sessionInfoPtr->writeDataFunction( sessionInfoPtr );
sessionInfoPtr->sendBufPos = sessionInfoPtr->sendBufStartOfs;
if( cryptStatusError( status ) )
sessionInfoPtr->writeErrorState = status;
return( status );
}
/* If there's too much data to fit in the buffer, send it through to the
host */
while( sessionInfoPtr->sendBufPos + dataLength >= \
protocolInfoPtr->sendBufMaxPos )
{
const int bytesToCopy = protocolInfoPtr->sendBufMaxPos - \
sessionInfoPtr->sendBufPos;
int status;
assert( bytesToCopy >= 0 && bytesToCopy <= dataLength );
/* Copy in as much data as we have room for and send it through */
if( bytesToCopy > 0 )
{
memcpy( sessionInfoPtr->sendBuffer + sessionInfoPtr->sendBufPos,
dataPtr, bytesToCopy );
sessionInfoPtr->sendBufPos += bytesToCopy;
dataPtr += bytesToCopy;
dataLength -= bytesToCopy;
}
status = sessionInfoPtr->writeDataFunction( sessionInfoPtr );
sessionInfoPtr->sendBufPos = sessionInfoPtr->sendBufStartOfs;
if( cryptStatusError( status ) )
{
sessionInfoPtr->writeErrorState = status;
return( status );
}
}
/* If there's anything left, it'll fit in the buffer, just copy it in */
if( dataLength > 0 )
{
assert( sessionInfoPtr->sendBufPos + dataLength < \
protocolInfoPtr->sendBufMaxPos );
memcpy( sessionInfoPtr->sendBuffer + sessionInfoPtr->sendBufPos,
dataPtr, dataLength );
sessionInfoPtr->sendBufPos += dataLength;
}
return( length );
}
/* Read a fixed-size packet header, called by the secure data session
routines to read the fixed header on a data packet. This is an atomic
read of out-of-band data that isn't part of the packet payload, so we
have to make sure that we've got the entire header before we can
continue:
| <- hdrSize -> |
----+---------------+--------
////| |
----+---------------+--------
^ ^
| |
bEnd partialHdr
The data is read into the read buffer starting at the end of the last
payload packet bEnd (this is safe because this function causes a
pipeline stall so no more data can be read until the header has been
read), the function returns CRYPT_ERROR_TIMEOUT until partialHdr reaches
the full header size */
int readFixedHeader( SESSION_INFO *sessionInfoPtr, const int headerSize )
{
BYTE *bufPtr = sessionInfoPtr->receiveBuffer + \
sessionInfoPtr->receiveBufEnd;
int status;
/* If it's the first attempt at reading the header, set the total byte
count */
if( sessionInfoPtr->partialHeaderLength <= 0 )
sessionInfoPtr->partialHeaderLength = headerSize;
else
bufPtr += headerSize - sessionInfoPtr->partialHeaderLength;
assert( sessionInfoPtr->partialHeaderLength > 0 && \
sessionInfoPtr->partialHeaderLength <= headerSize );
/* Clear the first few bytes of returned data to make sure that the
higher-level code always bails out if the read fails for some reason
without returning an error status */
memset( bufPtr, 0, min( headerSize, 8 ) );
/* Try and read the remaining header bytes */
status = sread( &sessionInfoPtr->stream, bufPtr,
sessionInfoPtr->partialHeaderLength );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
return( status );
}
/* If we didn't get the whole header, treat it as a timeout error */
if( status < sessionInfoPtr->partialHeaderLength )
{
/* If we timed out during the handshake phase, treat it as a hard
timeout error */
if( !( sessionInfoPtr->flags & SESSION_ISOPEN ) )
retExt( sessionInfoPtr, CRYPT_ERROR_TIMEOUT,
"Timeout during packet header read, only got %d of %d "
"bytes", status, headerSize );
/* We're in the data-processing stage, it's a soft timeout error */
sessionInfoPtr->partialHeaderLength -= status;
return( 0 );
}
/* We've got the whole header ready to process */
assert( sessionInfoPtr->partialHeaderLength == status );
sessionInfoPtr->partialHeaderLength = 0;
return( headerSize );
}
/****************************************************************************
* *
* Request/response Session Data Handling Functions *
* *
****************************************************************************/
/* Read/write a PKI (i.e.ASN.1-encoded) datagram */
int readPkiDatagram( SESSION_INFO *sessionInfoPtr )
{
int length, status;
assert( isWritePtr( sessionInfoPtr, SESSION_INFO ) );
/* Read the datagram */
sessionInfoPtr->receiveBufEnd = 0;
status = sread( &sessionInfoPtr->stream, sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufSize );
if( cryptStatusError( status ) )
{
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
return( status );
}
if( status < 4 )
/* Perform a sanity check on the length. This avoids some
assertions in the debug build, and provides somewhat more
specific information for the caller than the invalid encoding
error that we'd get later */
retExt( sessionInfoPtr, CRYPT_ERROR_UNDERFLOW,
"Invalid PKI message length %d", status );
/* Find out how much data we got and perform a firewall check that
everything is OK. We rely on this rather than the read byte count
since checking the ASN.1, which is the data that will actually be
processed, avoids any vagaries of server implementation oddities */
length = checkObjectEncoding( sessionInfoPtr->receiveBuffer, status );
if( cryptStatusError( length ) )
retExt( sessionInfoPtr, length, "Invalid PKI message encoding" );
sessionInfoPtr->receiveBufEnd = length;
return( CRYPT_OK );
}
int writePkiDatagram( SESSION_INFO *sessionInfoPtr )
{
int status;
assert( isWritePtr( sessionInfoPtr, SESSION_INFO ) );
assert( sessionInfoPtr->receiveBufEnd > 4 );
/* Write the datagram */
status = swrite( &sessionInfoPtr->stream, sessionInfoPtr->receiveBuffer,
sessionInfoPtr->receiveBufEnd );
if( cryptStatusError( status ) )
sNetGetErrorInfo( &sessionInfoPtr->stream,
sessionInfoPtr->errorMessage,
&sessionInfoPtr->errorCode );
sessionInfoPtr->receiveBufEnd = 0;
return( status );
}
/****************************************************************************
* *
* Session Attribute Handling Functions *
* *
****************************************************************************/
/* Handle data sent to or read from a session object */
static int processGetAttribute( SESSION_INFO *sessionInfoPtr,
void *messageDataPtr, const int messageValue )
{
int *valuePtr = ( int * ) messageDataPtr;
/* Handle the various information types */
switch( messageValue )
{
case CRYPT_OPTION_NET_CONNECTTIMEOUT:
if( sessionInfoPtr->connectTimeout == CRYPT_ERROR )
return( exitErrorNotInited( sessionInfoPtr,
CRYPT_ERROR_NOTINITED ) );
*valuePtr = sessionInfoPtr->connectTimeout;
return( CRYPT_OK );
case CRYPT_OPTION_NET_TIMEOUT:
if( sessionInfoPtr->timeout == CRYPT_ERROR )
return( exitErrorNotInited( sessionInfoPtr,
CRYPT_ERROR_NOTINITED ) );
*valuePtr = sessionInfoPtr->timeout;
return( CRYPT_OK );
case CRYPT_ATTRIBUTE_ERRORTYPE:
*valuePtr = sessionInfoPtr->errorType;
return( CRYPT_OK );
case CRYPT_ATTRIBUTE_ERRORLOCUS:
*valuePtr = sessionInfoPtr->errorLocus;
return( CRYPT_OK );
case CRYPT_ATTRIBUTE_BUFFERSIZE:
*valuePtr = sessionInfoPtr->receiveBufSize;
return( CRYPT_OK );
case CRYPT_ATTRIBUTE_INT_ERRORCODE:
*valuePtr = sessionInfoPtr->errorCode;
return( CRYPT_OK );
case CRYPT_SESSINFO_ACTIVE:
/* Only secure transport sessions can be persistently active,
request/response sessions are only active while the
transaction is in progress */
*valuePtr = sessionInfoPtr->iCryptInContext != CRYPT_ERROR && \
( sessionInfoPtr->flags & SESSION_ISOPEN ) ? \
TRUE : FALSE;
return( CRYPT_OK );
case CRYPT_SESSINFO_CONNECTIONACTIVE:
*valuePtr = ( sessionInfoPtr->flags & SESSION_ISOPEN ) ? \
TRUE : FALSE;
return( CRYPT_OK );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -