Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

515 lines
11 KiB

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corporation
//
// SYNOPSIS
//
// Defines the class EapProfile.
//
///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <algorithm>
#include "EapProfile.h"
#include <align.h>
EapProfile::EapProfile() throw ()
: begin(0),
end(0),
capacity(0)
{
}
EapProfile::~EapProfile() throw ()
{
Clear();
CoTaskMemFree(begin);
}
HRESULT EapProfile::Assign(const EapProfile& rhs) throw ()
{
if (this == &rhs)
{
return S_OK;
}
Clear();
Reserve(rhs.Size());
for (const ConfigData* i = rhs.begin; i != rhs.end; ++i)
{
end->length = i->length;
end->value = static_cast<BYTE*>(CoTaskMemAlloc(i->length));
if (end->value == 0)
{
Clear();
return E_OUTOFMEMORY;
}
memcpy(end->value, i->value, i->length);
++end;
}
return S_OK;
}
HRESULT EapProfile::Load(VARIANT& src) throw ()
{
HRESULT hr;
Clear();
if (V_VT(&src) == VT_EMPTY)
{
return S_OK;
}
if (V_VT(&src) != (VT_ARRAY | VT_VARIANT))
{
return DISP_E_TYPEMISMATCH;
}
const SAFEARRAY* sa = V_ARRAY(&src);
if (sa == 0)
{
return E_POINTER;
}
// Find the first and last elements of the VARIANT array.
VARIANT* first = static_cast<VARIANT*>(sa->pvData);
VARIANT* last = first + sa->rgsabound[0].cElements;
// Ensure that all the VARIANTs contain a valid config value.
for (const VARIANT* i = first; i != last; ++i)
{
hr = ValidateConfigChunk(*i);
if (FAILED(hr))
{
return hr;
}
}
// Sort the VARIANTs by type and sequence.
std::sort(first, last, LessThan);
// Gather the config for each type.
for (const VARIANT* j = first; j != last; )
{
BYTE type = ExtractString(*j)[0];
// Find the end of the type's config.
const VARIANT* typeEnd = j + 1;
while ((typeEnd != last) && (ExtractString(*typeEnd)[0] == type))
{
++typeEnd;
}
// Gather the config for this type.
hr = GatherAndAppend(j, typeEnd);
if (FAILED(hr))
{
return hr;
}
// Advance to the next type.
j = typeEnd;
}
return S_OK;
}
HRESULT EapProfile::Store(VARIANT& dst) throw ()
{
HRESULT hr;
// Clear the out parameter.
VariantInit(&dst);
// Computer the number of VARIANTs required.
DWORD nelem = 0;
for (const ConfigData* i = begin; i != end; ++i)
{
nelem += ChunksRequired(*i);
}
// Allocate the SAFEARRAY for the result.
SAFEARRAY* sa = SafeArrayCreateVector(VT_VARIANT, 0, nelem);
if (sa == 0)
{
return E_OUTOFMEMORY;
}
// Scatter the config into the SAFEARRAY.
VARIANT* nextValue = static_cast<VARIANT*>(sa->pvData);
for (const ConfigData* j = begin; j != end; ++j)
{
hr = Scatter(*j, nextValue);
if (FAILED(hr))
{
SafeArrayDestroy(sa);
return hr;
}
}
// Store the result.
V_VT(&dst) = VT_ARRAY | VT_VARIANT;
V_ARRAY(&dst) = sa;
return S_OK;
}
void EapProfile::Clear() throw ()
{
while (end > begin)
{
--end;
CoTaskMemFree(end->value);
}
}
void EapProfile::ClearExcept(BYTE type) throw ()
{
for (ConfigData* i = begin; i != end; )
{
if (i->value[0] != type)
{
// Free the config.
CoTaskMemFree(i->value);
// Decrement the number of elements.
--end;
// After Load completes we don't care if the array is sorted so we can
// just move the last element into the empty slot.
*i = *end;
}
else
{
++i;
}
}
}
void EapProfile::Erase(BYTE type) throw ()
{
for (ConfigData* i = begin; i != end; ++i)
{
if (i->value[0] == type)
{
// Free the config.
CoTaskMemFree(i->value);
// Decrement the number of elements.
--end;
// After Load completes we don't care if the array is sorted so we can
// just move the last element into the empty slot.
*i = *end;
break;
}
}
}
void EapProfile::Get(BYTE type, ConstConfigData& dst) const throw ()
{
for (const ConfigData* i = begin; i != end; ++i)
{
if (i->value[0] == type)
{
// The EAP DLL doesn't want the type byte.
dst.length = i->length - ALIGN_WORST;
dst.value = i->value + ALIGN_WORST;
return;
}
}
dst.length = 0;
dst.value = 0;
}
HRESULT EapProfile::Set(BYTE type, const ConstConfigData& newConfig) throw ()
{
if (newConfig.length == 0)
{
Erase(type);
return S_OK;
}
if (newConfig.value == 0)
{
return E_POINTER;
}
if (ChunksRequired(newConfig) > maxChunks)
{
return E_INVALIDARG;
}
// Ensure that we have room for the new element before we copy the value.
HRESULT hr = Reserve(Size() + 1);
if (FAILED(hr))
{
return hr;
}
// One extra byte for the type tag. Rounded to ALIGN_WORST
DWORD len = newConfig.length + ALIGN_WORST;
BYTE* val = static_cast<BYTE*>(CoTaskMemAlloc(len));
if (val == 0)
{
return E_OUTOFMEMORY;
}
// Erase any existing config for the type.
Erase(type);
// Start with the lead type byte.
val[0] = type;
// And then the rest of the configuration.
memcpy(val + ALIGN_WORST, newConfig.value, newConfig.length);
// Append the result.
end->length = len;
end->value = val;
++end;
return S_OK;
}
void EapProfile::Pop(ConfigData& dst) throw ()
{
--end;
dst = *end;
}
void EapProfile::Swap(EapProfile& other) throw ()
{
std::swap(begin, other.begin);
std::swap(end, other.end);
std::swap(capacity, other.capacity);
}
inline EapProfile::SeqNum EapProfile::ExtractSequence(const BYTE* src) throw ()
{
return (static_cast<SeqNum>(src[0]) << 8) | static_cast<SeqNum>(src[1]);
}
inline void EapProfile::InsertSequence(SeqNum seq, BYTE* dst) throw ()
{
dst[0] = static_cast<BYTE>((seq >> 8) & 0xFF);
dst[1] = static_cast<BYTE>(seq & 0xFF);
}
HRESULT EapProfile::GatherAndAppend(
const VARIANT* first,
const VARIANT* last
) throw ()
{
HRESULT hr = Reserve(Size() + 1);
if (FAILED(hr))
{
return hr;
}
// 1 type byte for the entire config.
DWORD len = ALIGN_WORST;
for (const VARIANT* i = first; i != last; ++i)
{
// Ignore the SDO header.
len += ExtractLength(*i) - sdoHeaderSize;
}
BYTE* val = static_cast<BYTE*>(CoTaskMemAlloc(len));
if (val == 0)
{
return E_OUTOFMEMORY;
}
end->length = len;
end->value = val;
++end;
// Get the type byte out of the first chunk.
val[0] = ExtractString(*first)[0];
// keep the real value aligned
val += ALIGN_WORST;
// Now concatenate the chunks, ignoring the header.
for (const VARIANT* j = first; j != last; ++j)
{
size_t chunkSize = (ExtractLength(*j) - sdoHeaderSize);
memcpy(val, (ExtractString(*j) + sdoHeaderSize), chunkSize);
val += chunkSize;
}
return S_OK;
}
HRESULT EapProfile::Scatter(
const ConstConfigData& src,
VARIANT*& dst
) throw ()
{
BYTE type = src.value[0];
const BYTE* val = src.value + ALIGN_WORST;
DWORD len = src.length - ALIGN_WORST;
// Sequence number.
SeqNum sequence = 0;
// Keep scattering until it's all gone.
while (len > 0)
{
// Compute the size of this chunk.
size_t chunkSize = (len > maxChunkSize) ? maxChunkSize : len;
// Create a SAFEARRAY of BYTEs to hold the data.
SAFEARRAY* sa = SafeArrayCreateVector(
VT_UI1,
0,
(chunkSize + sdoHeaderSize)
);
if (sa == 0)
{
return E_OUTOFMEMORY;
}
// Add the type byte and sequence number.
BYTE* chunk = static_cast<BYTE*>(sa->pvData);
chunk[0] = type;
InsertSequence(sequence, chunk + 1);
memcpy(chunk + sdoHeaderSize, val, chunkSize);
// Store it in the dst VARIANT.
V_VT(dst) = VT_ARRAY | VT_UI1;
V_ARRAY(dst) = sa;
++dst;
// Update our cursor.
val += chunkSize;
len -= chunkSize;
++sequence;
}
return S_OK;
}
HRESULT EapProfile::Reserve(size_t newCapacity) throw ()
{
if (newCapacity <= capacity)
{
return S_OK;
}
// Ensure we grow wisely.
const size_t minGrowth = (capacity < 4) ? 4 : ((capacity * 3) / 2);
if (newCapacity < minGrowth)
{
newCapacity = minGrowth;
}
// Allocate the new array.
size_t nbyte = newCapacity * sizeof(ConfigData);
ConfigData* newBegin = static_cast<ConfigData*>(
CoTaskMemAlloc(nbyte)
);
if (newBegin == 0)
{
return E_OUTOFMEMORY;
}
// Save the existing data.
memcpy(newBegin, begin, Size() * sizeof(ConfigData));
// Update the state.
end = newBegin + Size();
capacity = newCapacity;
// Now it's safe to free the old array and swap in the new.
CoTaskMemFree(begin);
begin = newBegin;
return S_OK;
}
inline size_t EapProfile::ChunksRequired(const ConstConfigData& str) throw ()
{
return ((str.length - ALIGN_WORST) + (maxChunkSize - 1)) / maxChunkSize;
}
inline DWORD EapProfile::ExtractLength(const VARIANT& src) throw ()
{
return V_ARRAY(&src)->rgsabound[0].cElements;
}
inline const BYTE* EapProfile::ExtractString(const VARIANT& src) throw ()
{
return static_cast<const BYTE*>(V_ARRAY(&src)->pvData);
}
bool EapProfile::LessThan(const VARIANT& lhs, const VARIANT& rhs) throw ()
{
const BYTE* val1 = ExtractString(lhs);
const BYTE* val2 = ExtractString(rhs);
// Sort first by type, then sequence.
if (val1[0] < val2[0])
{
return true;
}
else if (val1[0] == val2[0])
{
return ExtractSequence(val1 + 1) < ExtractSequence(val2 + 1);
}
else
{
return false;
}
}
HRESULT EapProfile::ValidateConfigChunk(const VARIANT& value) throw ()
{
if (V_VT(&value) != (VT_ARRAY | VT_UI1))
{
return DISP_E_TYPEMISMATCH;
}
const SAFEARRAY* sa = V_ARRAY(&value);
if (sa == 0)
{
return E_POINTER;
}
// The data has to be big enough for the header and 1 data byte.
if (sa->rgsabound[0].cElements <= sdoHeaderSize)
{
return E_INVALIDARG;
}
return S_OK;
}