747 lines
13 KiB
C++
747 lines
13 KiB
C++
/*++
|
|
|
|
Copyright (c) 2003 Microsoft Corporation
|
|
|
|
Module Name: IsapiBuffer.cpp
|
|
|
|
Abstract:
|
|
|
|
A simple buffer class
|
|
|
|
Author:
|
|
|
|
ISAPI developer (Microsoft employee), November 2002
|
|
|
|
--*/
|
|
|
|
#include <IsapiTools.h>
|
|
|
|
#define SIZE_ENCODED_CHUNK 4
|
|
#define SIZE_UNENCODED_CHUNK 3
|
|
|
|
//
|
|
// Local prototypes
|
|
//
|
|
|
|
BOOL
|
|
Base64DecodeChunk(
|
|
CHAR * szChunk,
|
|
BYTE * pData,
|
|
DWORD * pcbData
|
|
);
|
|
|
|
//
|
|
// Method implementations
|
|
//
|
|
|
|
ISAPI_BUFFER::ISAPI_BUFFER(
|
|
DWORD dwMaxAlloc
|
|
) : _pData( _InlineData ),
|
|
_cbData( 0 ),
|
|
_cbBuffer( INLINE_BUFFER_SIZE ),
|
|
_dwMaxAlloc( dwMaxAlloc ? dwMaxAlloc : DEFAULT_MAX_ALLOC_SIZE )
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Constructor for the ISAPI_BUFFER object
|
|
|
|
Arguments:
|
|
|
|
dwMaxAlloc - Initial value of max allocation size
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
return;
|
|
}
|
|
|
|
ISAPI_BUFFER::~ISAPI_BUFFER()
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Destructory for the ISAPI_BUFFER object
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Reset the object. Ensure that we
|
|
// deallocate any associated heap.
|
|
//
|
|
|
|
Reset( TRUE );
|
|
}
|
|
|
|
BOOL
|
|
ISAPI_BUFFER::SetData(
|
|
VOID * pData,
|
|
DWORD cbData
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Sets the specified data into the ISAPI_BUFFER object
|
|
|
|
Arguments:
|
|
|
|
pData - Pointer to the data to set
|
|
cbData - Size of the data to set
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Ensure that the buffer is sufficient to
|
|
// store the data.
|
|
//
|
|
|
|
if ( cbData > _cbBuffer &&
|
|
!Resize( cbData ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Copy the data into the buffer and
|
|
// set the new size
|
|
//
|
|
|
|
CopyMemory( _pData, pData, cbData );
|
|
|
|
_cbData = cbData;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ISAPI_BUFFER::AppendData(
|
|
VOID * pData,
|
|
DWORD cbData
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Appends data to the ISAPI_BUFFER object
|
|
|
|
Arguments:
|
|
|
|
pData - Pointer to the data to append
|
|
cbData - Size of the data to append
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
DWORD cbNewSize;
|
|
|
|
//
|
|
// Ensure that the buffer is sufficient to
|
|
// store the data.
|
|
//
|
|
|
|
cbNewSize = _cbData + cbData;
|
|
|
|
if ( cbNewSize > _cbBuffer &&
|
|
!Resize( cbNewSize ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Copy the data into the buffer, beginning
|
|
// at the specified offset, and set the new size.
|
|
//
|
|
|
|
CopyMemory( (BYTE*)_pData + _cbData, pData, cbData );
|
|
|
|
_cbData = cbNewSize;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ISAPI_BUFFER::Resize(
|
|
DWORD cbNewSize
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Ensures that the storage for ISAPI_BUFFER is sufficient to
|
|
contain the specified number of bytes
|
|
|
|
Arguments:
|
|
|
|
cbNewSize - The target size
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
HANDLE hNew;
|
|
DWORD cbAdjustedNewSize;
|
|
|
|
//
|
|
// If the buffer is already large enough, then
|
|
// just return TRUE.
|
|
//
|
|
|
|
if ( cbNewSize <= _cbBuffer )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If the new requested size is larger than the
|
|
// maximum we will allocate, then fail the call
|
|
//
|
|
|
|
if ( cbNewSize > _dwMaxAlloc )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// Adjust the new size so that we allocate blocks
|
|
// using a granularity of ISAPILIB_BUFFER_INLINE_SIZE.
|
|
// Also, make sure that this adjustment does not
|
|
// exceed _dwMaxAlloc.
|
|
//
|
|
|
|
cbAdjustedNewSize = cbNewSize / INLINE_BUFFER_SIZE * INLINE_BUFFER_SIZE;
|
|
|
|
if ( cbAdjustedNewSize < cbNewSize )
|
|
{
|
|
cbAdjustedNewSize += INLINE_BUFFER_SIZE;
|
|
}
|
|
|
|
if ( cbAdjustedNewSize > _dwMaxAlloc )
|
|
{
|
|
cbAdjustedNewSize = _dwMaxAlloc;
|
|
}
|
|
|
|
//
|
|
// Allocate the new storage.
|
|
//
|
|
|
|
if ( _pData == _InlineData )
|
|
{
|
|
//
|
|
// If _pData is currently pointed at the new buffer,
|
|
// then we need to allocate some heap storage.
|
|
//
|
|
|
|
hNew = LocalAlloc( NONZEROLPTR, cbAdjustedNewSize );
|
|
|
|
if ( hNew == NULL )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// If there was data in the old buffer, copy
|
|
// it to the new buffer.
|
|
//
|
|
|
|
if ( _cbData )
|
|
{
|
|
CopyMemory( (BYTE*)hNew, _pData, _cbData );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If _pData is already on the heap, then reallocate it.
|
|
//
|
|
|
|
hNew = LocalReAlloc( _pData, cbAdjustedNewSize, LMEM_MOVEABLE );
|
|
|
|
if ( hNew == NULL )
|
|
{
|
|
goto Failed;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Adjust the pointer and size of the new buffer
|
|
//
|
|
|
|
_pData = (BYTE*)hNew;
|
|
_cbBuffer = cbAdjustedNewSize;
|
|
|
|
return TRUE;
|
|
|
|
Failed:
|
|
|
|
//
|
|
// No special cleanup to be done
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
ISAPI_BUFFER::Reset(
|
|
BOOL fDealloc
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Resets the ISAPI_BUFFER object
|
|
|
|
Arguments:
|
|
|
|
fDealloc - If TRUE, then any heap associated with the
|
|
object is freed.
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If we are supposed to dealloc some
|
|
// heap memory, do so now.
|
|
//
|
|
|
|
if ( fDealloc &&
|
|
_pData != _InlineData )
|
|
{
|
|
LocalFree( _pData );
|
|
_pData = _InlineData;
|
|
_cbBuffer = INLINE_BUFFER_SIZE;
|
|
}
|
|
|
|
//
|
|
// Reset the data size
|
|
//
|
|
|
|
_cbData = 0;
|
|
}
|
|
|
|
BOOL
|
|
ISAPI_BUFFER::SetDataSize(
|
|
DWORD cbNewSize
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Resets the size member ISAPI_BUFFER object.
|
|
|
|
Arguments:
|
|
|
|
cbNewSize - The new size to set
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Don't allow it to be set to something bigger
|
|
// than the buffer
|
|
//
|
|
|
|
if ( cbNewSize > _cbBuffer )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
_cbData = cbNewSize;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
ISAPI_BUFFER::SetMaxAlloc(
|
|
DWORD dwMaxAlloc
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Sets the max alloc size of the ISAPI_BUFFER object.
|
|
|
|
Arguments:
|
|
|
|
dwMaxAlloc - The new size to set
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
_dwMaxAlloc = dwMaxAlloc ? dwMaxAlloc : DEFAULT_MAX_ALLOC_SIZE;
|
|
}
|
|
|
|
DWORD
|
|
ISAPI_BUFFER::QueryMaxAlloc(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Returns the max alloc size of the ISAPI_BUFFER object.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
The max alloc size
|
|
|
|
--*/
|
|
{
|
|
return _dwMaxAlloc;
|
|
}
|
|
|
|
VOID *
|
|
ISAPI_BUFFER::QueryPtr(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
returns a pointer to the data member ISAPI_BUFFER object.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
A pointer to the data member
|
|
|
|
--*/
|
|
{
|
|
return _pData;
|
|
}
|
|
|
|
DWORD
|
|
ISAPI_BUFFER::QueryDataSize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
returns the data size of the ISAPI_BUFFER object.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
The data size
|
|
|
|
--*/
|
|
{
|
|
return _cbData;
|
|
}
|
|
|
|
DWORD
|
|
ISAPI_BUFFER::QueryBufferSize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Returns the buffer size of the ISAPI_BUFFER object.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
The size of the buffer
|
|
|
|
--*/
|
|
{
|
|
return _cbBuffer;
|
|
}
|
|
|
|
BOOL
|
|
ISAPI_BUFFER::Base64Decode(
|
|
CHAR * szString
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Base64 decodes a string into ISAPI_BUFFER object.
|
|
|
|
Arguments:
|
|
|
|
szString - The string to decode
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
BYTE pData[SIZE_UNENCODED_CHUNK];
|
|
CHAR * pCursor;
|
|
DWORD cchRemaining;
|
|
DWORD cchString;
|
|
DWORD cbData;
|
|
BOOL fResult;
|
|
|
|
//
|
|
// Validate the input. Make sure that size the input data
|
|
// is evenly divisible by 4
|
|
//
|
|
|
|
cchString = strlen( szString );
|
|
|
|
if ( cchString % 4 )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
goto Failed;
|
|
}
|
|
|
|
//
|
|
// Decode the data in 4 byte chunks
|
|
//
|
|
|
|
pCursor = szString;
|
|
cchRemaining = cchString;
|
|
|
|
while ( cchRemaining )
|
|
{
|
|
fResult = Base64DecodeChunk( pCursor,
|
|
pData,
|
|
&cbData );
|
|
|
|
if ( !fResult )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
fResult = AppendData( pData, cbData );
|
|
|
|
if ( !fResult )
|
|
{
|
|
goto Failed;
|
|
}
|
|
|
|
cchRemaining -= SIZE_ENCODED_CHUNK;
|
|
pCursor += SIZE_ENCODED_CHUNK;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
Failed:
|
|
|
|
//
|
|
// No special cleanup required
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
ISAPI_BUFFER::ZeroBuffer(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Fills the ISAPI_BUFFER data buffer with zeros.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ZeroMemory( _pData, _cbBuffer );
|
|
}
|
|
|
|
BOOL
|
|
Base64DecodeChunk(
|
|
CHAR * szChunk,
|
|
BYTE * pData,
|
|
DWORD * pcbData
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Decodes a 4 byte base64 encoded chunk into 3 decoded bytes
|
|
|
|
Arguments:
|
|
|
|
szChunk - The chunk to decode
|
|
pData - The buffer to store the chunk
|
|
pcbData - Upon return, the number of bytes in the return buffer
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
DWORD x;
|
|
DWORD cbChunk;
|
|
|
|
BYTE szReverseTable[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
|
|
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
|
|
0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
|
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
|
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
|
0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
|
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
|
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
|
0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
|
|
//
|
|
// Clear only the necessary output bytes.
|
|
//
|
|
// *** WARNING ***
|
|
// We are making two assumptions here. First, we assume
|
|
// that pData is big enough to do this. Second, we assume
|
|
// that szChunk is always 4 bytes. If either of these
|
|
// assumptions are bad, we'll fail in a bad way.
|
|
//
|
|
|
|
if ( szChunk[2] == '=' )
|
|
{
|
|
*pcbData = 1;
|
|
cbChunk = 2;
|
|
}
|
|
else if ( szChunk[3] == '=' )
|
|
{
|
|
*pcbData = 2;
|
|
cbChunk = 3;
|
|
}
|
|
else
|
|
{
|
|
*pcbData = 3;
|
|
cbChunk = 4;
|
|
}
|
|
|
|
ZeroMemory( pData, *pcbData );
|
|
|
|
//
|
|
// Reverse lookup the character mappings
|
|
//
|
|
|
|
for ( x = 0; x < cbChunk; x++ )
|
|
{
|
|
szChunk[x] = szReverseTable[szChunk[x]];
|
|
|
|
if ( szChunk[x] == 0xff )
|
|
{
|
|
//
|
|
// Hmmm. This isn't a valid character
|
|
// for a base64 encoded buffer.
|
|
//
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Shift bits per Base64 rules (originally defined in
|
|
// RFC 1341, section 5.2)
|
|
//
|
|
|
|
for ( x = 0; x < cbChunk; x++ )
|
|
{
|
|
switch ( x )
|
|
{
|
|
case 0:
|
|
|
|
pData[0] = szChunk[0] << 2;
|
|
break;
|
|
|
|
case 1:
|
|
|
|
pData[0] |= ( szChunk[1] & 0x30 ) >> 4;
|
|
pData[1] = ( szChunk[1] & 0xf ) << 4;
|
|
break;
|
|
|
|
case 2:
|
|
|
|
pData[1] |= ( szChunk[2] & 0x3c ) >> 2;
|
|
pData[2] = ( szChunk[2] & 0x3 ) << 6;
|
|
break;
|
|
|
|
case 3:
|
|
|
|
pData[2] |= szChunk[3];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|