|
|
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corporation.
//
// SYNOPSIS
//
// Provides function definitions for the Internet Authentication Service
// Template Library (IASTL).
//
///////////////////////////////////////////////////////////////////////////////
//////////
// IASTL must be used in conjuction with ATL.
//////////
#ifndef __ATLCOM_H__
#error iastlimp.cpp requires atlcom.h to be included first
#endif
#include <windows.h>
//////////
// IASTL declarations.
//////////
#include <iastl.h>
//////////
// The entire library is contained within the IASTL namespace.
//////////
namespace IASTL {
//////////
// FSM governing IAS components.
//////////
const IASComponent::State IASComponent::fsm[NUM_EVENTS][NUM_STATES] = { { STATE_UNINITIALIZED, STATE_UNEXPECTED, STATE_UNEXPECTED, STATE_UNEXPECTED }, { STATE_UNEXPECTED, STATE_INITIALIZED, STATE_INITIALIZED, STATE_UNEXPECTED }, { STATE_UNEXPECTED, STATE_UNEXPECTED, STATE_SUSPENDED, STATE_SUSPENDED }, { STATE_UNEXPECTED, STATE_UNEXPECTED, STATE_INITIALIZED, STATE_INITIALIZED }, { STATE_SHUTDOWN, STATE_SHUTDOWN, STATE_UNEXPECTED, STATE_SHUTDOWN } };
///////////////////////////////////////////////////////////////////////////////
//
// IASComponent
//
///////////////////////////////////////////////////////////////////////////////
HRESULT IASComponent::fireEvent(Event event) throw () { // Check the input parameters.
if (event >= NUM_EVENTS) { return E_UNEXPECTED; }
// Compute the next state.
State next = fsm[event][state];
Lock();
HRESULT hr;
if (next == state) { // If we're already in that state, there's nothing to do.
hr = S_OK; } else if (next == STATE_UNEXPECTED) { // We received an unexpected event.
hr = E_UNEXPECTED; } else { // Attempt the transition.
hr = attemptTransition(event);
// Only change state if the transition was successful.
if (SUCCEEDED(hr)) { state = next; } }
Unlock();
return hr; }
///////////////////////////////////////////////////////////////////////////////
//
// IASRequestHandler
//
///////////////////////////////////////////////////////////////////////////////
STDMETHODIMP IASRequestHandler::OnRequest(IRequest* pRequest) { if (getState() != IASComponent::STATE_INITIALIZED) { pRequest->SetResponse(IAS_RESPONSE_DISCARD_PACKET, IAS_INTERNAL_ERROR); pRequest->ReturnToSource(IAS_REQUEST_STATUS_ABORT); } else { onAsyncRequest(pRequest); }
return S_OK; }
///////////////////////////////////////////////////////////////////////////////
//
// IASRequestHandlerSync
//
///////////////////////////////////////////////////////////////////////////////
void IASRequestHandlerSync::onAsyncRequest(IRequest* pRequest) throw () { pRequest->ReturnToSource(onSyncRequest(pRequest)); }
//////////
// End of the IASTL namespace.
//////////
}
//////////
// Only pull in the utility classes if necessary.
//////////
#ifdef IASTLUTL_H
//////////
// The utility classes are also contained within the IASTL namespace.
//////////
namespace IASTL {
///////////////////////////////////////////////////////////////////////////////
//
// IASAttribute
//
///////////////////////////////////////////////////////////////////////////////
IASAttribute& IASAttribute::operator=(PIASATTRIBUTE attr) throw () { // Check for self-assignment.
if (p != attr) { _release(); p = attr; _addref(); } return *this; }
void IASAttribute::attach(PIASATTRIBUTE attr, bool addRef) throw () { _release(); p = attr; if (addRef) { _addref(); } }
bool IASAttribute::load(IAttributesRaw* request, DWORD dwId) { // Release any existing attribute.
release();
DWORD posCount = 1; ATTRIBUTEPOSITION pos; HRESULT hr = request->GetAttributes(&posCount, &pos, 1, &dwId); if (FAILED(hr)) { if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { // There was more than one, so release the partial result.
IASAttributeRelease(pos.pAttribute); } issue_error(hr); }
p = (posCount ? pos.pAttribute : NULL);
return p != NULL; }
bool IASAttribute::load(IAttributesRaw* request, DWORD dwId, IASTYPE itType) { // Get an attribute with the given ID.
load(request, dwId);
// Attributes have a fixed type, so if the type doesn't match it must
// be an error.
if (p && p->Value.itType != itType) { release(); issue_error(DISP_E_TYPEMISMATCH); }
return p != NULL; }
void IASAttribute::store(IAttributesRaw* request) const { if (p) { ATTRIBUTEPOSITION pos; pos.pAttribute = p; HRESULT hr = request->AddAttributes(1, &pos); if (FAILED(hr)) { issue_error(hr); } } }
void IASAttribute::setOctetString(DWORD dwLength, const BYTE* lpValue) { PBYTE newVal = NULL;
if (dwLength) { // Allocate a buffer for the octet string ...
newVal = (PBYTE)CoTaskMemAlloc(dwLength); if (!newVal) { issue_error(E_OUTOFMEMORY); }
// ... and copy it in.
if (lpValue != NULL) { memcpy(newVal, lpValue, dwLength); } }
// Clear the old value.
clearValue();
// Store the new.
p->Value.OctetString.lpValue = newVal; p->Value.OctetString.dwLength = dwLength; p->Value.itType = IASTYPE_OCTET_STRING; }
void IASAttribute::setOctetString(PCSTR szAnsi) { setOctetString((szAnsi ? strlen(szAnsi) : 0), (const BYTE*)szAnsi); }
void IASAttribute::setOctetString(PCWSTR szWide) { // Allocate a buffer for the conversion.
int len = WideCharToMultiByte(CP_ACP, 0, szWide, -1, NULL, 0, NULL, NULL); PSTR ansi = (PSTR)_alloca(len);
// Convert from wide to ansi.
len = WideCharToMultiByte(CP_ACP, 0, szWide, -1, ansi, len, NULL, NULL);
// Don't include the null-terminator.
if (len) { --len; }
// Set the octet string.
setOctetString(len, (const BYTE*)ansi); }
void IASAttribute::setString(DWORD dwLength, const BYTE* lpValue) { // Reserve space for a null terminator.
LPSTR newVal = (LPSTR)CoTaskMemAlloc(dwLength + 1); if (!newVal) { issue_error(E_OUTOFMEMORY); }
// Copy in the string ...
memcpy(newVal, lpValue, dwLength); // ... and add a null terminator.
newVal[dwLength] = 0;
// Clear the old value.
clearValue();
// Store the new value.
p->Value.String.pszAnsi = newVal; p->Value.String.pszWide = NULL; p->Value.itType = IASTYPE_STRING;
}
void IASAttribute::setString(PCSTR szAnsi) { LPSTR newVal = NULL;
if (szAnsi) { // Allocate a buffer for the string ...
size_t nbyte = strlen(szAnsi) + 1; newVal = (LPSTR)CoTaskMemAlloc(nbyte); if (!newVal) { issue_error(E_OUTOFMEMORY); }
// ... and copy it in.
memcpy(newVal, szAnsi, nbyte); }
// Clear the old value.
clearValue();
// Store the new value.
p->Value.String.pszAnsi = newVal; p->Value.String.pszWide = NULL; p->Value.itType = IASTYPE_STRING; }
void IASAttribute::setString(PCWSTR szWide) { LPWSTR newVal = NULL;
if (szWide) { // Allocate a buffer for the string ...
size_t nbyte = sizeof(WCHAR) * (wcslen(szWide) + 1); newVal = (LPWSTR)CoTaskMemAlloc(nbyte); if (!newVal) { issue_error(E_OUTOFMEMORY); }
// ... and copy it in.
memcpy(newVal, szWide, nbyte); }
// Clear the old value.
clearValue();
// Store the new value.
p->Value.String.pszWide = newVal; p->Value.String.pszAnsi = NULL; p->Value.itType = IASTYPE_STRING; }
void IASAttribute::clearValue() throw () { switch (p->Value.itType) { case IASTYPE_STRING: CoTaskMemFree(p->Value.String.pszAnsi); CoTaskMemFree(p->Value.String.pszWide); break;
case IASTYPE_OCTET_STRING: CoTaskMemFree(p->Value.OctetString.lpValue); break; }
p->Value.itType = IASTYPE_INVALID; }
///////////////////////////////////////////////////////////////////////////////
//
// IASAttributePosition
//
///////////////////////////////////////////////////////////////////////////////
IASAttributePosition& IASAttributePosition::operator=( const ATTRIBUTEPOSITION& rhs ) throw () { operator=(rhs.pAttribute); pos.dwReserved = rhs.dwReserved; return *this; }
IASAttributePosition& IASAttributePosition::operator=( IASATTRIBUTE* rhs ) throw () { if (rhs != getAttribute()) { _release(); pos.pAttribute = rhs; _addref(); } return *this; }
///////////////////////////////////////////////////////////////////////////////
//
// IASAttributeVector
//
///////////////////////////////////////////////////////////////////////////////
IASAttributeVector::IASAttributeVector() throw () : begin_(NULL), end_(NULL), capacity_(0), owner(false) { }
IASAttributeVector::IASAttributeVector(size_type N) : begin_(NULL), end_(NULL), capacity_(0), owner(false) { reserve(N); }
IASAttributeVector::IASAttributeVector( PATTRIBUTEPOSITION init, size_type initCap ) throw () : begin_(init), end_(begin_), capacity_(initCap), owner(false) { }
IASAttributeVector::IASAttributeVector(const IASAttributeVector& v) : begin_(NULL), end_(NULL), capacity_(0), owner(false) { reserve(v.size());
for (const_iterator i = v.begin(); i != v.end(); ++i, ++end_) { *end_ = *i;
IASAttributeAddRef(end_->pAttribute); } }
IASAttributeVector& IASAttributeVector::operator=(const IASAttributeVector& v) { if (this != &v) { clear();
reserve(v.size());
for (const_iterator i = v.begin(); i != v.end(); ++i, ++end_) { *end_ = *i;
IASAttributeAddRef(end_->pAttribute); } }
return *this; }
IASAttributeVector::~IASAttributeVector() throw () { clear();
if (owner && begin_) { CoTaskMemFree(begin_); } }
bool IASAttributeVector::contains(DWORD attrID) const throw () { for (const_iterator i = begin(); i != end(); ++i) { if ((i->pAttribute != 0) && (i->pAttribute->dwId == attrID)) { return true; } }
return false; }
IASAttributeVector::iterator IASAttributeVector::discard(iterator p) throw () { // We now have one less attribute.
--end_;
// Shift over one.
memmove(p, p + 1, (size_t)((PBYTE)end_ - (PBYTE)p));
// The iterator now points to the next element, so no need to update.
return p; }
IASAttributeVector::iterator IASAttributeVector::fast_discard(iterator p) throw () { // We now have one less attribute.
--end_;
// Use the attribute from the end to fill the empty slot.
*p = *end_;
return p; }
DWORD IASAttributeVector::load( IAttributesRaw* request, DWORD attrIDCount, LPDWORD attrIDs ) { clear();
// Get the desired attributes.
DWORD posCount = capacity_; HRESULT hr = request->GetAttributes(&posCount, begin_, attrIDCount, attrIDs); end_ = begin_ + posCount;
if (FAILED(hr)) { // Maybe the array just wasn't big enough.
if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { // Clear the partial result.
clear();
// Find out how much space we really need.
DWORD needed = 0; hr = request->GetAttributes(&needed, NULL, attrIDCount, attrIDs); if (FAILED(hr)) { issue_error(hr); }
// Reserve the necessary space ...
reserve(needed);
// ... and try again.
return load(request, attrIDCount, attrIDs); }
end_ = begin_;
issue_error(hr); }
return posCount; }
DWORD IASAttributeVector::load(IAttributesRaw* request) { // Find out how much space we need.
DWORD needed; HRESULT hr = request->GetAttributeCount(&needed); if (FAILED(hr)) { issue_error(hr); }
// Make sure we have enough space.
reserve(needed);
return load(request, 0, NULL); }
void IASAttributeVector::push_back(ATTRIBUTEPOSITION& p, bool addRef) throw () { // Make sure we have enough room for one more attribute.
if (size() == capacity()) { reserve(empty() ? 1 : 2 * size()); }
if (addRef) { IASAttributeAddRef(p.pAttribute); }
// Store the attribute at the end.
*end_ = p;
// Move the end.
++end_; }
void IASAttributeVector::remove(IAttributesRaw* request) { if (begin_ != end_) { HRESULT hr = request->RemoveAttributes(size(), begin_); if (FAILED(hr)) { issue_error(hr); } } }
void IASAttributeVector::store(IAttributesRaw* request) const { if (begin_ != end_) { HRESULT hr = request->AddAttributes(size(), begin_); if (FAILED(hr)) { issue_error(hr); } } }
void IASAttributeVector::clear() throw () { while (end_ != begin_) { --end_; IASAttributeRelease(end_->pAttribute); } }
void IASAttributeVector::reserve(size_type N) { // We only worry about growing; shrinking is a nop.
if (N > capacity_) { // Allocate memory for the new vector.
PATTRIBUTEPOSITION tmp = (PATTRIBUTEPOSITION)CoTaskMemAlloc(N * sizeof(ATTRIBUTEPOSITION)); if (tmp == NULL) { issue_error(E_OUTOFMEMORY); }
// Copy the existing attributes into the new array.
size_type size_ = size(); memcpy(tmp, begin_, size_ * sizeof(ATTRIBUTEPOSITION));
// Free the old array if necessary.
if (owner) { CoTaskMemFree(begin_); }
// Update our state to point at the new array.
begin_ = tmp; end_ = begin_ + size_; capacity_ = N; owner = true; } }
///////////////////////////////////////////////////////////////////////////////
//
// IASRequest
//
///////////////////////////////////////////////////////////////////////////////
IASRequest::IASRequest(IRequest* request) : req(request) { if (!req) { // We don't allow NULL request objects.
issue_error(E_POINTER); }
// Get the 'raw' counterpart.
checkError(req->QueryInterface(__uuidof(IAttributesRaw), (PVOID*)&raw)); }
IASRequest& IASRequest::operator=(const IASRequest& request) throw () { // Check for self-assignment.
if (this != &request) { _release(); req = request.req; raw = request.raw; _addref(); } return *this; }
DWORD IASRequest::GetAttributes(DWORD dwPosCount, PATTRIBUTEPOSITION pPositions, DWORD dwAttrIDCount, LPDWORD lpdwAttrIDs) { DWORD count = dwPosCount; HRESULT hr = raw->GetAttributes(&count, pPositions, dwAttrIDCount, lpdwAttrIDs); if (FAILED(hr)) { if (hr == HRESULT_FROM_WIN32(ERROR_MORE_DATA)) { // Free the partial results.
while (count--) { IASAttributeRelease(pPositions->pAttribute); pPositions->pAttribute = NULL; ++pPositions; } }
issue_error(hr); }
return count; }
IASDictionary::IASDictionary( const WCHAR* const* selectNames, PCWSTR path ) : mapSize(0), nextRowNumber(0), currentRow(NULL) { if (!path) { // No path, so get the cached local dictionary.
table = IASGetLocalDictionary(); if (!table) { issue_error(GetLastError()); } } else { // Otherwise, get an arbitrary dictionary.
HRESULT hr = IASGetDictionary(path, &data, &storage); if (FAILED(hr)) { issue_error(hr); } table = &data; }
// How many columns are selected ?
for (const WCHAR* const* p = selectNames; *p; ++p) { ++mapSize; }
// Allocate memory for the map.
selectMap = (PULONG)CoTaskMemAlloc(mapSize * sizeof(ULONG)); if (!selectMap) { issue_error(E_OUTOFMEMORY); }
// Lookup the column names.
for (ULONG i = 0; i < mapSize; ++i) { // If it begins with a '-', it's optional.
bool optional = false; const wchar_t* name = selectNames[i]; if (*name == L'-') { optional = true; ++name; }
// Find the column number.
ULONG columnNumber = 0; while ((columnNumber < table->numColumns) && (_wcsicmp(name, table->columnNames[columnNumber]) != 0)) { ++columnNumber; }
if ((columnNumber == table->numColumns) && !optional) { // We didn't find the column.
CoTaskMemFree(selectMap); issue_error(E_INVALIDARG); }
selectMap[i] = columnNumber; } }
IASDictionary::~IASDictionary() throw () { CoTaskMemFree(selectMap); }
bool IASDictionary::next() throw () { // Are there any rows left ?
if (nextRowNumber >= table->numRows) { return false; }
// Set currentRow to the next row.
currentRow = table->table + nextRowNumber * table->numColumns;
// Advance nextRowNumber.
++nextRowNumber;
return true; }
void IASDictionary::reset() throw () { nextRowNumber = 0; currentRow = NULL; }
bool IASDictionary::isEmpty(ULONG ordinal) const { return V_VT(getVariant(ordinal)) == VT_EMPTY; }
VARIANT_BOOL IASDictionary::getBool(ULONG ordinal) const { const VARIANT* v = getVariant(ordinal);
if (V_VT(v) == VT_BOOL) { return V_BOOL(v); } else if (V_VT(v) != VT_EMPTY) { issue_error(DISP_E_TYPEMISMATCH); }
return VARIANT_FALSE; }
BSTR IASDictionary::getBSTR(ULONG ordinal) const { const VARIANT* v = getVariant(ordinal);
if (V_VT(v) == VT_BSTR) { return V_BSTR(v); } else if (V_VT(v) != VT_EMPTY) { issue_error(DISP_E_TYPEMISMATCH); }
return NULL; }
LONG IASDictionary::getLong(ULONG ordinal) const { const VARIANT* v = getVariant(ordinal);
if (V_VT(v) == VT_I4) { return V_I4(v); } else if (V_VT(v) != VT_EMPTY) { issue_error(DISP_E_TYPEMISMATCH); }
return 0L; }
const VARIANT* IASDictionary::getVariant(ULONG ordinal) const { static VARIANT empty;
// Are we positioned on a row ?
if (!currentRow) { issue_error(E_UNEXPECTED); }
// Is the ordinal valid ?
if (ordinal >= mapSize) { issue_error(E_INVALIDARG); }
// Get the table column.
ULONG column = selectMap[ordinal];
// If it's out of range, this must have been an optional field, so return
// an empty VARIANT.
return (column >= table->numColumns) ? &empty : currentRow + column; }
//////////
// End of the IASTL namespace.
//////////
}
///////////////////////////////////////////////////////////////////////////////
//
// OctetString conversion macros and functions.
//
///////////////////////////////////////////////////////////////////////////////
PSTR IASOctetStringToAnsi(const IAS_OCTET_STRING& src, PSTR dst) throw () { dst[src.dwLength] = '\0'; return (PSTR)memcpy(dst, src.lpValue, src.dwLength); }
PWSTR IASOctetStringToWide(const IAS_OCTET_STRING& src, PWSTR dst) throw () { DWORD nChar = MultiByteToWideChar(CP_ACP, 0, (PSTR)src.lpValue, src.dwLength, dst, src.dwLength); dst[nChar] = L'\0'; return dst; }
///////////////////////////////////////////////////////////////////////////////
//
// Miscellaneous functions.
//
///////////////////////////////////////////////////////////////////////////////
// Returns 'true' if the IASTYPE maps to a RADIUS integer attribute.
bool isRadiusInteger(IASTYPE type) throw () { bool retval;
switch (type) { case IASTYPE_BOOLEAN: case IASTYPE_INTEGER: case IASTYPE_ENUM: retval = true; break;
default: retval = false; }
return retval; }
PIASATTRIBUTE IASPeekAttribute( IAttributesRaw* request, DWORD dwId, IASTYPE itType ) throw () { if (request) { DWORD posCount = 1; ATTRIBUTEPOSITION pos; HRESULT hr = request->GetAttributes(&posCount, &pos, 1, &dwId);
if (posCount == 1) { IASAttributeRelease(pos.pAttribute);
if (SUCCEEDED(hr)) { // There is some confusion between RAS and IAS regarding which
// IASTYPEs to use for various RADIUS attributes, so we'll assume
// any of the RADIUS integer types are interchangeable.
if (itType == pos.pAttribute->Value.itType || (isRadiusInteger(itType) && isRadiusInteger(pos.pAttribute->Value.itType))) { return pos.pAttribute; } } } }
return NULL; }
//////////
// End of utility classe.
//////////
#endif // IASTLUTL_H
|