355 lines
8.1 KiB
C++
355 lines
8.1 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) 1999, Microsoft Corp. All rights reserved.
|
|
//
|
|
// FILE
|
|
//
|
|
// pipeline.cpp
|
|
//
|
|
// SYNOPSIS
|
|
//
|
|
// Defines the class Pipeline.
|
|
//
|
|
// MODIFICATION HISTORY
|
|
//
|
|
// 01/28/2000 Original version.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <polcypch.h>
|
|
#include <iasattr.h>
|
|
#include <pipeline.h>
|
|
#include <request.h>
|
|
#include <sdoias.h>
|
|
#include <stage.h>
|
|
#include <new>
|
|
|
|
STDMETHODIMP Pipeline::InitNew()
|
|
{ return S_OK; }
|
|
|
|
STDMETHODIMP Pipeline::Initialize()
|
|
{
|
|
// Set up the Provider-Type for NAS-State requests.
|
|
if (IASAttributeAlloc(1, &proxy.pAttribute) != NO_ERROR)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
proxy.pAttribute->dwId = IAS_ATTRIBUTE_PROVIDER_TYPE;
|
|
proxy.pAttribute->Value.itType = IASTYPE_ENUM;
|
|
proxy.pAttribute->Value.Enumerator = IAS_PROVIDER_RADIUS_PROXY;
|
|
|
|
// Allocate the TLS use for storing thread state.
|
|
tlsIndex = TlsAlloc();
|
|
if (tlsIndex == (DWORD)-1)
|
|
{
|
|
HRESULT hr = GetLastError();
|
|
return HRESULT_FROM_WIN32(hr);
|
|
}
|
|
|
|
// Read the configuration from the registry.
|
|
HKEY key;
|
|
LONG error = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Services\\RemoteAccess"
|
|
L"\\Policy\\Pipeline",
|
|
0,
|
|
KEY_READ,
|
|
&key
|
|
);
|
|
if (!error)
|
|
{
|
|
error = readConfiguration(key);
|
|
RegCloseKey(key);
|
|
}
|
|
|
|
if (error) { return HRESULT_FROM_WIN32(error); }
|
|
|
|
// Initialize the stages.
|
|
for (Stage* s = begin; s != end; ++s)
|
|
{
|
|
HRESULT hr = initializeStage(s);
|
|
if (FAILED(hr)) { return hr; }
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Pipeline::Suspend()
|
|
{ return S_OK; }
|
|
|
|
STDMETHODIMP Pipeline::Resume()
|
|
{ return S_OK; }
|
|
|
|
STDMETHODIMP Pipeline::Shutdown()
|
|
{
|
|
delete[] begin;
|
|
begin = end = NULL;
|
|
|
|
SafeArrayDestroy(handlers);
|
|
handlers = NULL;
|
|
|
|
TlsFree(tlsIndex);
|
|
tlsIndex = (DWORD)-1;
|
|
|
|
IASAttributeRelease(proxy.pAttribute);
|
|
proxy.pAttribute = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Pipeline::GetProperty(LONG Id, VARIANT* pValue)
|
|
{
|
|
return DISP_E_MEMBERNOTFOUND;
|
|
}
|
|
|
|
STDMETHODIMP Pipeline::PutProperty(LONG Id, VARIANT* pValue)
|
|
{
|
|
if (Id) { return S_OK; }
|
|
|
|
if (V_VT(pValue) != (VT_ARRAY | VT_VARIANT)) { return DISP_E_TYPEMISMATCH; }
|
|
|
|
SafeArrayDestroy(handlers);
|
|
handlers = NULL;
|
|
return SafeArrayCopy(V_ARRAY(pValue), &handlers);
|
|
}
|
|
|
|
STDMETHODIMP Pipeline::OnRequest(IRequest* pRequest) throw ()
|
|
{
|
|
// Extract the Request object.
|
|
Request* request = Request::narrow(pRequest);
|
|
if (!request) { return E_NOINTERFACE; }
|
|
|
|
// Classify the request.
|
|
classify(*request);
|
|
|
|
// Set this as the new source.
|
|
request->pushSource(this);
|
|
|
|
// Set the next stage to execute, i.e., stage zero.
|
|
request->pushState(0);
|
|
|
|
// Execute the request.
|
|
execute(*request);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Pipeline::OnRequestComplete(
|
|
IRequest* pRequest,
|
|
IASREQUESTSTATUS eStatus
|
|
)
|
|
{
|
|
// Extract the Request object.
|
|
Request* request = Request::narrow(pRequest);
|
|
if (!request) { return E_NOINTERFACE; }
|
|
|
|
// If TLS is set, then we're on the original thread ...
|
|
if (TlsGetValue(tlsIndex))
|
|
{
|
|
// ... so clear the value to let the thread know we finished.
|
|
TlsSetValue(tlsIndex, NULL);
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, we're completing asynchronously so continue execution on
|
|
// the caller's thread.
|
|
execute(*request);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
Pipeline::Pipeline() throw ()
|
|
: tlsIndex((DWORD)-1),
|
|
begin(NULL),
|
|
end(NULL),
|
|
handlers(NULL)
|
|
{
|
|
memset(&proxy, 0, sizeof(proxy));
|
|
}
|
|
|
|
Pipeline::~Pipeline() throw ()
|
|
{
|
|
Shutdown();
|
|
}
|
|
|
|
void Pipeline::classify(
|
|
Request& request
|
|
) throw ()
|
|
{
|
|
IASREQUEST routingType = request.getRequest();
|
|
|
|
switch (routingType)
|
|
{
|
|
case IAS_REQUEST_ACCESS_REQUEST:
|
|
{
|
|
PIASATTRIBUTE state = request.findFirst(
|
|
RADIUS_ATTRIBUTE_STATE
|
|
);
|
|
if (state && state->Value.OctetString.dwLength)
|
|
{
|
|
routingType = IAS_REQUEST_CHALLENGE_RESPONSE;
|
|
}
|
|
break;
|
|
}
|
|
case IAS_REQUEST_ACCOUNTING:
|
|
{
|
|
PIASATTRIBUTE status = request.findFirst(
|
|
RADIUS_ATTRIBUTE_ACCT_STATUS_TYPE
|
|
);
|
|
if (status)
|
|
{
|
|
switch (status->Value.Integer)
|
|
{
|
|
case 7: // Accounting-On
|
|
case 8: // Accounting-Off
|
|
{
|
|
// NAS state messages always go to RADIUS proxy.
|
|
request.AddAttributes(1, &proxy);
|
|
routingType = IAS_REQUEST_NAS_STATE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
request.setRoutingType(routingType);
|
|
}
|
|
|
|
BOOL Pipeline::executeNext(
|
|
Request& request
|
|
) throw ()
|
|
{
|
|
// Compute the next stage to try.
|
|
Stage* nextStage = begin + request.popState();
|
|
|
|
// Find the next stage that wants to handle the request.
|
|
while (nextStage != end && !nextStage->shouldHandle(request))
|
|
{
|
|
++nextStage;
|
|
}
|
|
|
|
// Have we reached the end of the pipeline ?
|
|
if (nextStage == end)
|
|
{
|
|
// Reset the source property.
|
|
request.popSource();
|
|
|
|
// We're done.
|
|
request.ReturnToSource(IAS_REQUEST_STATUS_HANDLED);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Save the next stage to try.
|
|
request.pushState(nextStage - begin + 1);
|
|
|
|
// Set TLS, so we'll know we're executing a request.
|
|
TlsSetValue(tlsIndex, (PVOID)-1);
|
|
|
|
// Forward to the handler.
|
|
nextStage->onRequest(&request);
|
|
|
|
// If TLS is not set, then the request completed synchronously.
|
|
BOOL keepExecuting = !TlsGetValue(tlsIndex);
|
|
|
|
// Clear TLS.
|
|
TlsSetValue(tlsIndex, NULL);
|
|
|
|
return keepExecuting;
|
|
}
|
|
|
|
LONG Pipeline::readConfiguration(HKEY key) throw ()
|
|
{
|
|
// How many stages do we have ?
|
|
LONG error;
|
|
DWORD subKeys;
|
|
error = RegQueryInfoKeyW(
|
|
key,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&subKeys,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (error) { return error; }
|
|
|
|
// Is the pipeline empty ?
|
|
if (!subKeys) { return NO_ERROR; }
|
|
|
|
// Allocate memory to hold the stages.
|
|
begin = new (std::nothrow) Stage[subKeys];
|
|
if (!begin) { return ERROR_NOT_ENOUGH_MEMORY; }
|
|
|
|
end = begin;
|
|
|
|
// Read the configuration for each stage.
|
|
for (DWORD i = 0; i < subKeys; ++i)
|
|
{
|
|
WCHAR name[32];
|
|
DWORD nameLen = 32;
|
|
error = RegEnumKeyExW(
|
|
key,
|
|
i,
|
|
name,
|
|
&nameLen,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (error)
|
|
{
|
|
if (error == ERROR_NO_MORE_ITEMS) { error = NO_ERROR; }
|
|
break;
|
|
}
|
|
|
|
error = (end++)->readConfiguration(key, name);
|
|
if (error) { break; }
|
|
}
|
|
|
|
// Sort the stages according to priority.
|
|
qsort(
|
|
begin,
|
|
end - begin,
|
|
sizeof(Stage),
|
|
(CompFn)Stage::sortByPriority
|
|
);
|
|
|
|
return error;
|
|
}
|
|
|
|
HRESULT Pipeline::initializeStage(Stage* stage) throw ()
|
|
{
|
|
VARIANT *beginHandlers, *endHandlers;
|
|
if (handlers)
|
|
{
|
|
ULONG nelem = handlers->rgsabound[1].cElements;
|
|
beginHandlers = (VARIANT*)handlers->pvData;
|
|
endHandlers = beginHandlers + nelem * 2;
|
|
}
|
|
else
|
|
{
|
|
beginHandlers = endHandlers = NULL;
|
|
}
|
|
|
|
// Did we get this handler from the SDOs ?
|
|
for (VARIANT* v = beginHandlers; v != endHandlers; v+= 2)
|
|
{
|
|
if (!_wcsicmp(stage->getProgID(), V_BSTR(v)))
|
|
{
|
|
// Yes, so just use the one they gave us.
|
|
return stage->setHandler(V_UNKNOWN(++v));
|
|
}
|
|
}
|
|
|
|
// No, so create a new one.
|
|
return stage->createHandler();
|
|
}
|