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
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;
|
|
}
|