windows-nt/Source/XPSP1/NT/net/ias/protocol/proxy/radproxy.h
2020-09-26 16:20:57 +08:00

508 lines
13 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000, Microsoft Corp. All rights reserved.
//
// FILE
//
// radproxy.h
//
// SYNOPSIS
//
// Declares the interface into the reusable RadiusProxy engine. This should
// have no IAS specific dependencies.
//
// MODIFICATION HISTORY
//
// 02/08/2000 Original version.
// 05/30/2000 Eliminate QUESTIONABLE state.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef RADPROXY_H
#define RADPROXY_H
#if _MSC_VER >= 1000
#pragma once
#endif
#include <iascache.h>
#include <iasobj.h>
#include <radshare.h>
#include <timerq.h>
#include <udpsock.h>
struct RadiusAttribute;
struct RadiusPacket;
class Request;
class Session;
///////////////////////////////////////////////////////////////////////////////
//
// CLASS
//
// RemotePort
//
// DESCRIPTION
//
// Describes a remote endpoint for a RADIUS conversation.
//
///////////////////////////////////////////////////////////////////////////////
class RemotePort
{
public:
// Read-only properties.
const InternetAddress address;
const RadiusOctets secret;
RemotePort(
ULONG ipAddress,
USHORT port,
PCSTR sharedSecret
);
RemotePort(const RemotePort& port);
// Returns a packet identifier to use when sending a request to this port.
BYTE getIdentifier() throw ()
{ return (BYTE)++nextIdentifier; }
// Synchronizes this with the state of 'port', i.e., use the same next
// identifier.
void copyState(const RemotePort& port) throw ()
{ nextIdentifier = port.nextIdentifier; }
bool operator==(const RemotePort& p) const throw ()
{ return address == p.address && secret == p.secret; }
private:
Count nextIdentifier;
// Not implemented.
RemotePort& operator=(RemotePort&);
};
///////////////////////////////////////////////////////////////////////////////
//
// struct
//
// RemoteServerConfig
//
// DESCRIPTION
//
// Plain ol' data holding all the configuration associated with a
// RemoteServer. This spares clients from having to call a monster
// contructor when creating a remote server.
//
///////////////////////////////////////////////////////////////////////////////
struct RemoteServerConfig
{
GUID guid;
ULONG ipAddress;
USHORT authPort;
USHORT acctPort;
PCSTR authSecret;
PCSTR acctSecret;
ULONG priority;
ULONG weight;
ULONG timeout;
ULONG maxLost;
ULONG blackout;
bool sendSignature;
bool sendAcctOnOff;
};
///////////////////////////////////////////////////////////////////////////////
//
// CLASS
//
// RemoteServer
//
// DESCRIPTION
//
// Describes a remote RADIUS server and maintains the state of that server.
//
///////////////////////////////////////////////////////////////////////////////
class RemoteServer
{
public:
DECLARE_REFERENCE_COUNT();
// Unique ID for this server.
const GUID guid;
// Authentication and accounting ports.
RemotePort authPort;
RemotePort acctPort;
// Read-only properties for load-balancing and failover.
const ULONG priority;
const ULONG weight;
// Read-only properties for determining server state.
const ULONG timeout;
const LONG maxLost;
const ULONG blackout;
// Should we always send a Signature attribute?
const bool sendSignature;
// Should we formard Accounting-On/Off requests?
const bool sendAcctOnOff;
RemoteServer(const RemoteServerConfig& config);
// Returns the servers IP address.
ULONG getAddress() const throw ()
{ return authPort.address.sin_addr.s_addr; }
// Returns 'true' if the server is available for use.
bool isAvailable() const throw ()
{ return state == AVAILABLE; }
// Returns 'true' if the server should receive a broadcast.
bool shouldBroadcast() throw ();
// Notifies the RemoteServer that a valid packet has been received. Returns
// true if this triggers a state change.
bool onReceive() throw ();
// Notfies the RemoteServer that a request has timed out. Returns true if
// this triggers a state change.
bool onTimeout() throw ();
// Synchronize the state of this server with target.
void copyState(const RemoteServer& target) throw ();
bool operator==(const RemoteServer& s) const throw ();
protected:
// This is virtual so that RemoteServer can server as a base class.
virtual ~RemoteServer() throw () { }
private:
// Possible states of the server.
enum State
{
AVAILABLE, // Server is available for use.
UNAVAILABLE, // Not available but may receive broadcasts.
LOCKED // Locked by a thread; prevents multiple threads from
// simultaneously broadcasting to a server.
};
// Transition the server state; returns 'true' if successful.
bool changeState(State from, State to) throw ();
State state; // Current state of the server.
LONG lostCount; // Number of packets lost since last received.
ULONG64 expiry; // Time when blackout interval expires.
// Not implemented.
RemoteServer& operator=(RemoteServer&);
};
typedef ObjectPointer<RemoteServer> RemoteServerPtr;
typedef ObjectVector<RemoteServer> RemoteServers;
class RequestStack;
class ProxyContext;
///////////////////////////////////////////////////////////////////////////////
//
// CLASS
//
// ServerGroup
//
// DESCRIPTION
//
// Load balances requests among a group of RemoteServers.
//
///////////////////////////////////////////////////////////////////////////////
class ServerGroup
{
public:
DECLARE_REFERENCE_COUNT();
ServerGroup(
PCWSTR groupName,
RemoteServer* const* first,
RemoteServer* const* last
);
// Returns the number of servers in the group.
ULONG size() const throw ()
{ return servers.size(); }
bool isEmpty() const throw ()
{ return servers.empty(); }
// Name used to identify the group.
PCWSTR getName() const throw ()
{ return name; }
// Returns a collection of servers that should receive the request.
void getServersForRequest(
ProxyContext* context,
BYTE packetCode,
RequestStack& result
) const;
// Methods for iterating the servers in the group.
RemoteServers::iterator begin() const throw ()
{ return servers.begin(); }
RemoteServers::iterator end() const throw ()
{ return servers.end(); }
private:
~ServerGroup() throw () { }
// Pick a server from the list. The list must not be empty, and all the
// servers must have the same priority.
static RemoteServer* pickServer(
RemoteServers::iterator first,
RemoteServers::iterator last
) throw ();
// Array of servers in priority order.
RemoteServers servers;
// End of top priority level in array.
RemoteServers::iterator endTopPriority;
// Maximum number of bytes required to hold the server candidates.
ULONG maxCandidatesSize;
RadiusString name;
static ULONG theSeed;
// Not implemented.
ServerGroup(const ServerGroup&);
ServerGroup& operator=(const ServerGroup&);
};
typedef ObjectPointer<ServerGroup> ServerGroupPtr;
typedef ObjectVector<ServerGroup> ServerGroups;
///////////////////////////////////////////////////////////////////////////////
//
// CLASS
//
// ServerGroupManager
//
// DESCRIPTION
//
// Manages a collection of ServerGroups.
//
///////////////////////////////////////////////////////////////////////////////
class ServerGroupManager
{
public:
ServerGroupManager() throw () { }
// Set the server groups to be managed.
bool setServerGroups(
ServerGroups::iterator begin,
ServerGroups::iterator end
) throw ();
// Returns a server with the given IP address.
RemoteServerPtr findServer(
ULONG address
) const throw ();
void getServersByGroup(
ProxyContext* context,
BYTE packetCode,
PCWSTR name,
RequestStack& result
) const;
void getServersForAcctOnOff(
ProxyContext* context,
RequestStack& result
) const;
private:
// Synchronize access.
mutable RWLock monitor;
// Server groups being managed sorted by name.
ServerGroups groups;
// All servers sorted by guid.
RemoteServers byAddress;
// All servers sorted by guid.
RemoteServers byGuid;
// Servers to receive Accounting-On/Off requests.
RemoteServers acctServers;
// Not implemented.
ServerGroupManager(const ServerGroupManager&);
ServerGroupManager& operator=(const ServerGroupManager&);
};
class RadiusProxyClient;
///////////////////////////////////////////////////////////////////////////////
//
// CLASS
//
// RadiusProxyEngine
//
// DESCRIPTION
//
// Implements a RADIUS proxy.
//
///////////////////////////////////////////////////////////////////////////////
class RadiusProxyEngine : PacketReceiver
{
public:
// Final result of processing a request.
enum Result
{
resultSuccess,
resultNotEnoughMemory,
resultUnknownServerGroup,
resultUnknownServer,
resultInvalidRequest,
resultSendError,
resultRequestTimeout
};
RadiusProxyEngine(RadiusProxyClient* source);
~RadiusProxyEngine() throw ();
// Set the server groups to be used by the proxy.
bool setServerGroups(
ServerGroup* const* begin,
ServerGroup* const* end
) throw ();
// Forward a request to the given server group.
void forwardRequest(
PVOID context,
PCWSTR serverGroup,
BYTE code,
const BYTE* requestAuthenticator,
const RadiusAttribute* begin,
const RadiusAttribute* end
) throw ();
// Callback when a request context has been abandoned.
static void onRequestAbandoned(
PVOID context,
RemoteServer* server
) throw ();
// Callback when a request has timed out.
static void onRequestTimeout(
Request* request
) throw ();
private:
// Methods for associating a stateful authentication session with a
// particular server.
RemoteServerPtr getServerAffinity(
const RadiusPacket& packet
) throw ();
void setServerAffinity(
const RadiusPacket& packet,
RemoteServer& server
) throw ();
// PacketReceiver callbacks.
virtual void onReceive(
UDPSocket& socket,
ULONG_PTR key,
const SOCKADDR_IN& remoteAddress,
BYTE* buffer,
ULONG bufferLength
) throw ();
virtual void onReceiveError(
UDPSocket& socket,
ULONG_PTR key,
ULONG errorCode
) throw ();
// Forward a request to an individual RemoteServer.
Result sendRequest(
RadiusPacket& packet,
Request* request
);
// Report an event to the client.
void reportEvent(
const RadiusEvent& event
) const throw ();
void reportEvent(
RadiusEvent& event,
RadiusEventType type
) const throw ();
// Callback when a timer has expired.
static VOID NTAPI onTimerExpiry(PVOID context, BOOLEAN flag) throw ();
// The object supplying us with requests.
RadiusProxyClient* client;
// The local address of the proxy. Used when forming Proxy-State.
ULONG proxyAddress;
// UDP sockets used for network I/O.
UDPSocket authSock;
UDPSocket acctSock;
// Server groups used for processing groups.
ServerGroupManager groups;
// Table of pending requests.
HashTable< LONG, Request > pending;
// Queue of pending requests.
TimerQueue timers;
// Table of current authentication sessions.
Cache< RadiusRawOctets, Session > sessions;
// Global pointer to the RadiusProxyEngine. This is a hack, but it saves me
// from having to give every Request and Context object a back pointer.
static RadiusProxyEngine* theProxy;
// Not implemented.
RadiusProxyEngine(const RadiusProxyEngine&);
RadiusProxyEngine& operator=(const RadiusProxyEngine&);
};
///////////////////////////////////////////////////////////////////////////////
//
// CLASS
//
// RadiusProxyClient
//
// DESCRIPTION
//
// Abstract base class for clients of the RadiusProxy engine.
//
///////////////////////////////////////////////////////////////////////////////
class __declspec(novtable) RadiusProxyClient
{
public:
// Invoked to report one of the above events.
virtual void onEvent(
const RadiusEvent& event
) throw () = 0;
// Invoked exactly once for each call to RadiusProxyEngine::forwardRequest.
virtual void onComplete(
RadiusProxyEngine::Result result,
PVOID context,
RemoteServer* server,
BYTE code,
const RadiusAttribute* begin,
const RadiusAttribute* end
) throw () = 0;
};
#endif // RADPROXY_H