3333 lines
99 KiB
C++
3333 lines
99 KiB
C++
#include "precomp.h"
|
|
#include "cnpcoder.h"
|
|
DEBUG_FILEZONE(ZONE_T120_MCSNC);
|
|
/*
|
|
* control.cpp
|
|
*
|
|
* Copyright (c) 1993 - 1996 by DataBeam Corporation, Lexington, KY
|
|
*
|
|
* Abstract:
|
|
* This is the implementation file for the MCS controller. Its primary
|
|
* purpose is to create and destroy objects as needed at run-time. The
|
|
* interface file contains a detailed description of what this class
|
|
* does.
|
|
*
|
|
* There can be only one instance of this object within an MCS provider.
|
|
* Once initialization is complete, this object performs all activity
|
|
* as part of an owner callback from one of its "children".
|
|
*
|
|
* The owner callback member function determines which callback is
|
|
* occurring, unpacks the parameters, and calls a private member function
|
|
* that is associated with that callback. So when reading through the
|
|
* code, it is possible to view those private member functions as though
|
|
* they were direct calls from the child objects. It is worth noting
|
|
* that all of the owner callback private member functions follow the
|
|
* same naming convention. The function name is the name of the
|
|
* originating object followed by the name of the operation. For
|
|
* example, if an application interface object sends a create domain
|
|
* request to the controller through the owner callback, the name of
|
|
* the associated member function is ApplicationCreateDomain. When a
|
|
* connection object wishes to delete itself, this is called
|
|
* ConnectionDeleteConnection.
|
|
*
|
|
* The Windows version of the constructor can optionally allocate a
|
|
* Windows timer to provider MCS with a heartbeat. The timer procedure
|
|
* uses a static variable to "jump into" the context of the controller
|
|
* object. For this reason, there can only be one instance of this class.
|
|
*
|
|
* This class is also responsible for sending four different messages
|
|
* to the node controller: connect provider indication; connect provider
|
|
* confirm; disconnect provider indication; and transport status
|
|
* indication. A control queue is maintained to buffer these indications
|
|
* and confirms until the next time slice.
|
|
*
|
|
* Private Instance Variables:
|
|
* Connection_Handle_Counter
|
|
* A rolling counter used by the controller to generate connection
|
|
* handles. It is 16-bit, and will not repeat a handle until all 64K
|
|
* have been used (0 is NOT a valid handle).
|
|
* ASN_Coder
|
|
* When using the DataBeam's implementation of ASN.1, this is the
|
|
* ASN coder. When the MCS coder is created, this object is passed
|
|
* to it, but this instance variable allows us to later delete this
|
|
* object.
|
|
* m_DomainList2
|
|
* A list of existing Domains, indexed by DomainSelector. These are
|
|
* created when the controller receives a CreateDomain.
|
|
* m_ConnectionList2
|
|
* A list of existing Connections, indexed by ConnectionHandle. These
|
|
* are created in one of two ways. First, in response to a locally
|
|
* generated ConnectProviderRequest (with a valid local domain and
|
|
* transport address). Second, in response to a locally generated
|
|
* ConnectProviderResponse (which is responding to an incoming
|
|
* connection).
|
|
* m_ConnPollList
|
|
* This is a list of active connection objects which is used for
|
|
* polling in the heartbeat call. The order of the entries is modified
|
|
* every heartbeat in order to provide fair access to resources.
|
|
* m_ConnPendingList2
|
|
* This is a list of incoming connections for which a connect provider
|
|
* response has not yet been received. This list holds pertinent
|
|
* information about the pending connection that will not be passed
|
|
* back in the ConnectProviderResponse..
|
|
* m_ConnectionDeletionList2
|
|
* A list of connection objects awaiting deletion,
|
|
* Connection_Deletion_Pending
|
|
* A flag that indicates whether or not there is anything in the
|
|
* connection deletion list.
|
|
* Private Member Functions:
|
|
* LoadTransportStacks
|
|
* This function is called by the constructor to load all available
|
|
* transport stacks for use by MCS. It reads the INI file to
|
|
* determine which DLLs are to be used, and then instantiate a
|
|
* transport interface object for each. This code is NOT portable.
|
|
* ApplicationCreateDomain
|
|
* This is an owner callback function that results from a call to
|
|
* CreateDomain. This callback comes from the application interface
|
|
* object that represents the originator of the request. The named
|
|
* domain will be created (if it doesn't already exist).
|
|
* ApplicationDeleteDomain
|
|
* This is an owner callback function that results from a call to
|
|
* DeleteDomain. This callback comes from the application interface
|
|
* object that represents the originator of the request. The named
|
|
* domain will be deleted (if it exists).
|
|
* ApplicationConnectProviderRequest
|
|
* This is an owner callback function that occurs when the node
|
|
* controller calls ConnectProviderRequest. After parameters are
|
|
* validated, a new connection object will be created.
|
|
* ApplicationConnectProviderResponse
|
|
* This is an owner callback function that occurs when the node
|
|
* controller calls ConnectProviderResponse. The controller responds
|
|
* by sending a message to the proper domain object, letting it know
|
|
* whether an inbound connection was accepted or rejected.
|
|
* ApplicationDisconnectProviderRequest
|
|
* This is an owner callback function that occurs when the node
|
|
* controller calls DisconnectProviderRequest. If the connection
|
|
* handle is valid, the associated connection object will be deleted.
|
|
* ApplicationAttachUserRequest
|
|
* This is an owner callback function that occurs when any application
|
|
* sends an AttachUserRequest via one of the active application
|
|
* interface objects. The controller will create a new user object
|
|
* after parameter validation.
|
|
* ConnectionDeleteConnection
|
|
* This is an owner callback function that occurs when a connection
|
|
* object determines the need to delete itself. This can occur for
|
|
* two reasons. First, in response to a disconnect provider ultimatum
|
|
* from either the local or the remote domain. Second, in response
|
|
* to a loss of connection from the transport layer.
|
|
* ConnectionConnectProviderConfirm
|
|
* This is an owner callback function that occurs when a connection
|
|
* object receives a connect response PDU from a remote provider for an
|
|
* outstanding connect initial. The controller responds by sending a
|
|
* connect provider confirm to the node controller.
|
|
* TransportDataIndication
|
|
* This is an owner callback function that occurs when data is
|
|
* received on a transport connection for which no other object has
|
|
* registered.
|
|
* TransportStatusIndication
|
|
* This is an owner callback function that occurs when a status
|
|
* indication message comes from the transport layer. This information
|
|
* is forwarded to the node controller in the form of a transport
|
|
* status indication message.
|
|
* ProcessConnectInitial
|
|
* Processes incoming connect initial PDUs. Sends connect provider
|
|
* indication to the node controller.
|
|
* ProcessConnectAdditional
|
|
* Processes incoming connect additional PDUs. Binds them to the
|
|
* appropriate connection, if possible.
|
|
* ConnectResponse
|
|
* Issues a failed connect response when something goes wrong.
|
|
* ConnectResult
|
|
* Issues a failed connect result when something goes wrong.
|
|
* AllocateConnectionHandle
|
|
* This private member function is used by the controller to allocate
|
|
* new connection handles when creating a new connection object.
|
|
* FlushMessageQueue
|
|
* This member function flushes the control message queue by sending
|
|
* all contained messages to the node controller.
|
|
*
|
|
* Caveats:
|
|
* There can only one instance of this object at a time.
|
|
*
|
|
* Author:
|
|
* James P. Galvin, Jr.
|
|
*/
|
|
|
|
/*
|
|
* External Interfaces
|
|
*/
|
|
|
|
#include <nmqos.h>
|
|
#include <t120qos.h>
|
|
#include "csap.h"
|
|
|
|
/*
|
|
* Macros
|
|
*/
|
|
enum
|
|
{
|
|
TRANSPORT_TRANSMIT_EVENT,
|
|
CONNECTION_DELETION_PENDING_EVENT,
|
|
GCC_FLUSH_OUTGOING_PDU_EVENT,
|
|
|
|
NUMBER_OF_EVENTS
|
|
};
|
|
|
|
/*
|
|
* Macros
|
|
*
|
|
* These macros define the maximum length of various strings within the
|
|
* controller. They are used when reading data from the INI file, which is
|
|
* very Windows specific. These values are somewhat arbitrary and may be
|
|
* changed in future releases, if necessary.
|
|
*/
|
|
#define MAXIMUM_CONFIGURATION_ITEM_LENGTH 20
|
|
#define MAXIMUM_TRANSPORT_IDENTIFIER_LENGTH 40
|
|
|
|
/* The MSMCSTCP window class name. This name must be unique system-wide. */
|
|
#define MSMCSTCP_WINDOW_CLASS_NAME "NM TCP Window"
|
|
|
|
/* Timer duration. We can get a timer event every X milliseconds. During
|
|
* this time, we can do any maintenance that is necessary. */
|
|
#define MSMCSTCP_TIMER_DURATION 30000
|
|
|
|
/*
|
|
* This is the number of extra memory blocks that the local memory
|
|
* manager can allocate.
|
|
* This number should be set to 3 * prime number and close to the
|
|
* maximum number of extra memory blocks that can be allocated.
|
|
*/
|
|
#define DEFAULT_MAX_EXTERNAL_MEMORY 237
|
|
|
|
/*
|
|
* This is a prototype for the controller thread entry point.
|
|
*/
|
|
ULong APIENTRY ControllerThread (PVoid);
|
|
|
|
// The DLL's HINSTANCE.
|
|
extern HINSTANCE g_hDllInst;
|
|
|
|
// The TCP socket window handle
|
|
HWND TCP_Window_Handle;
|
|
|
|
// The global MCS Critical Section
|
|
CRITICAL_SECTION g_MCS_Critical_Section;
|
|
|
|
/*
|
|
* This is a global variable that has a pointer to the one MCS coder that
|
|
* is instantiated by the MCS Controller. Most objects know in advance
|
|
* whether they need to use the MCS or the GCC coder, so, they do not need
|
|
* this pointer in their constructors.
|
|
*/
|
|
extern CMCSCoder *g_MCSCoder;
|
|
|
|
extern CCNPCoder *g_CNPCoder;
|
|
|
|
// The global TransportInterface pointer (for transport access)
|
|
extern PTransportInterface g_Transport;
|
|
|
|
BOOL GetSecurityInfo(ConnectionHandle connection_handle, PBYTE pInfo, PDWORD pcbInfo);
|
|
/*
|
|
* g_pMCSController
|
|
* This is a pointer to the one-and-only controller created within the
|
|
* MCS system. This object is created during MCSInitialize by the process
|
|
* that is taking on the responsibilities of the node controller.
|
|
*/
|
|
PController g_pMCSController = NULL;
|
|
|
|
// The MCS main thread handle
|
|
HANDLE g_hMCSThread = NULL;
|
|
|
|
/*
|
|
* These macros define the number of buckets to be used in various hash
|
|
* dictionaries that are maintained by the controller. Having more buckets
|
|
* allows the dictionaries to handle more entries efficiently, but costs
|
|
* more resources.
|
|
*/
|
|
#define CONNECTION_LIST_NUMBER_OF_BUCKETS 16
|
|
|
|
/*
|
|
* Controller ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This is the constructor for the Controller. It creates the application
|
|
* interface and transport interface objects that will be used by MCS.
|
|
* It also creates the memory manager object that will used throughout
|
|
* the system by anyone requiring memory management services. And its
|
|
* last duty is to allocate a Windows timer for use in getting a time
|
|
* slice within which MCS does its work.
|
|
*/
|
|
Controller::Controller
|
|
(
|
|
PMCSError mcs_error
|
|
)
|
|
:
|
|
CRefCount(MAKE_STAMP_ID('M','C','t','r')),
|
|
m_DomainList2(),
|
|
m_ConnectionList2(CONNECTION_LIST_NUMBER_OF_BUCKETS),
|
|
m_ConnPendingList2(),
|
|
m_ConnectionDeletionList2(CONNECTION_LIST_NUMBER_OF_BUCKETS)
|
|
{
|
|
ULong thread_id;
|
|
TransportInterfaceError transport_interface_error;
|
|
|
|
/*
|
|
* Initialize the return value to indicate that no error has yet occured.
|
|
*/
|
|
*mcs_error = MCS_NO_ERROR;
|
|
|
|
// Perform memory pool allocation for DataPacket objects.
|
|
DataPacket::AllocateMemoryPool (ALLOCATE_DATA_PACKET_OBJECTS);
|
|
|
|
/*
|
|
* Give all pointers and handles initial values so that the destructor
|
|
* will not try to free unallocated resources when the constructor fails
|
|
* part-way.
|
|
*/
|
|
TCP_Window_Handle = NULL;
|
|
Transport_Transmit_Event = NULL;
|
|
Connection_Deletion_Pending_Event = NULL;
|
|
m_fControllerThreadActive = FALSE;
|
|
#ifndef NO_TCP_TIMER
|
|
Timer_ID = 0;
|
|
#endif /* NO_TCP_TIMER */
|
|
|
|
/*
|
|
* Initialize the handle counters to 0. These rolling instance variables
|
|
* are used to generate uniwue handles as each user and connection object
|
|
* is created.
|
|
*/
|
|
Connection_Handle_Counter = 0;
|
|
Connection_Deletion_Pending = FALSE;
|
|
|
|
// Initialize MCS's critical section.
|
|
InitializeCriticalSection (&g_MCS_Critical_Section);
|
|
|
|
/*
|
|
* Create an ASN.1 coder which will encode all ASN.1 PDUs. Check
|
|
* to make sure the coder was successfully created.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
g_MCSCoder = new CMCSCoder ();
|
|
|
|
/*
|
|
* Make sure the creation of the packet coder was successful before
|
|
* proceeding.
|
|
*/
|
|
if (g_MCSCoder == NULL)
|
|
{
|
|
/*
|
|
* If the packet coder could not be createdm then report the error.
|
|
* This IS a fatal error, so the faulty controller should be
|
|
* destroyed and never used.
|
|
*/
|
|
WARNING_OUT (("Controller::Controller: failure creating packet coder"));
|
|
*mcs_error = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
|
|
/*
|
|
* Do not continue with the initialization if an error has occured.
|
|
*/
|
|
if (*mcs_error == MCS_NO_ERROR)
|
|
{
|
|
// We have to initialize the User class
|
|
if (FALSE == User::InitializeClass()) {
|
|
/*
|
|
* The initialization of the User class failed, so we
|
|
* must fail the creation of this controller.
|
|
*/
|
|
WARNING_OUT (("Controller::Controller: "
|
|
"failed to initialize User class."));
|
|
*mcs_error = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do not continue with the initialization if an error has occured.
|
|
*/
|
|
if (*mcs_error == MCS_NO_ERROR)
|
|
{
|
|
/*
|
|
* We must allocate an event object that will used to notify the
|
|
* controller when data is ready to be transmitted to a transport
|
|
* stack.
|
|
*/
|
|
Transport_Transmit_Event = CreateEvent (NULL, FALSE, FALSE, NULL);
|
|
|
|
if (Transport_Transmit_Event == NULL)
|
|
{
|
|
/*
|
|
* Were unable to allocate an event object for this task, so we
|
|
* must fail the creation of this controller.
|
|
*/
|
|
WARNING_OUT (("Controller::Controller: "
|
|
"failure allocating transport transmit event object"));
|
|
*mcs_error = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do not continue with the initialization if an error has occured.
|
|
*/
|
|
if (*mcs_error == MCS_NO_ERROR)
|
|
{
|
|
/*
|
|
* We must allocate an event object that will used for
|
|
* synchronization between the event loop thread and the thread
|
|
* that creates/destroys the Controller object.
|
|
*/
|
|
Synchronization_Event = CreateEvent (NULL, FALSE, FALSE, NULL);
|
|
|
|
if (Synchronization_Event == NULL)
|
|
{
|
|
/*
|
|
* Were unable to allocate an event object for this task, so we
|
|
* must fail the creation of this controller.
|
|
*/
|
|
WARNING_OUT (("Controller::Controller: "
|
|
"failure allocating synchronization event object"));
|
|
*mcs_error = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do not continue with the initialization if an error has occured.
|
|
*/
|
|
if (*mcs_error == MCS_NO_ERROR)
|
|
{
|
|
/*
|
|
* We must allocate an event object that will used to notify the
|
|
* controller when data is ready to be transmitted to a transport
|
|
* stack.
|
|
*/
|
|
Connection_Deletion_Pending_Event = CreateEvent (NULL, FALSE, FALSE, NULL);
|
|
|
|
if (Connection_Deletion_Pending_Event == NULL)
|
|
{
|
|
/*
|
|
* Were unable to allocate an event object for this task, so we
|
|
* must fail the creation of this controller.
|
|
*/
|
|
WARNING_OUT (("Controller::Controller: "
|
|
"failure allocating connection deletion pending event object"));
|
|
*mcs_error = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do not continue with the initialization if an error has occured.
|
|
*/
|
|
if (*mcs_error == MCS_NO_ERROR)
|
|
{
|
|
/*
|
|
* Initialize the flag that indicates that the controller is not yet
|
|
* shutting down.
|
|
*/
|
|
Controller_Closing = FALSE;
|
|
|
|
/*
|
|
* Since everything else was successful, we must create a thread
|
|
* winthin which the controller will do most of its work.
|
|
*/
|
|
g_hMCSThread = CreateThread (NULL, 0, ControllerThread,
|
|
(PVoid) this, 0, &thread_id);
|
|
|
|
if (g_hMCSThread == NULL)
|
|
{
|
|
/*
|
|
* We were unable to create the thread that the controller needs
|
|
* to do its job in an event-driven fashion. We must therefore
|
|
* fail the creation of this controller.
|
|
*/
|
|
WARNING_OUT (("Controller::Controller: failure creating thread"));
|
|
*mcs_error = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (*mcs_error == MCS_NO_ERROR) {
|
|
// We need to wait until the event loop thread creates the TCP msg window.
|
|
WaitForSingleObject (Synchronization_Event, INFINITE);
|
|
if (TCP_Window_Handle == NULL) {
|
|
WARNING_OUT (("Controller::Controller: The event-loop thread failed to create the TCP msg window."));
|
|
*mcs_error = MCS_NO_TRANSPORT_STACKS;
|
|
|
|
/*
|
|
* We assume that the event loop thread has exited.
|
|
*/
|
|
ClosePh (&g_hMCSThread);
|
|
}
|
|
else {
|
|
/*
|
|
* We set the flag used by the destructor
|
|
* to check whether we should wait for the thread to finish.
|
|
*/
|
|
m_fControllerThreadActive = TRUE;
|
|
}
|
|
}
|
|
|
|
if (*mcs_error == MCS_NO_ERROR)
|
|
{
|
|
g_CNPCoder = new CCNPCoder();
|
|
if (g_CNPCoder != NULL)
|
|
{
|
|
g_CNPCoder->Init();
|
|
}
|
|
else
|
|
{
|
|
WARNING_OUT(("Controller::Controller: "
|
|
"failuer allocating CNP Coder"));
|
|
*mcs_error = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now, load the transport interface.
|
|
*/
|
|
if (*mcs_error == MCS_NO_ERROR)
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
g_Transport = new TransportInterface (Transport_Transmit_Event,
|
|
&transport_interface_error);
|
|
|
|
/*
|
|
* Make sure the creation of the object was successful before
|
|
* proceeding.
|
|
*/
|
|
if (g_Transport != NULL)
|
|
{
|
|
/*
|
|
* Check the return value from the constructor.
|
|
*/
|
|
if (transport_interface_error == TRANSPORT_INTERFACE_NO_ERROR)
|
|
{
|
|
/*
|
|
* If everything was successful, put the new transport
|
|
* interface object into the dictionary.
|
|
*/
|
|
WARNING_OUT (("Controller::Controller: "
|
|
"TCP transport interface has been created successfully."));
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the return value indicates that something went
|
|
* wrong during the creation of the transport interface
|
|
* object, then must destroy it immediately to insure
|
|
* that it does not get used.
|
|
*/
|
|
WARNING_OUT (("Controller::Controller: "
|
|
"deleting faulty TCP transport interface"));
|
|
delete g_Transport;
|
|
g_Transport = NULL;
|
|
*mcs_error = MCS_NO_TRANSPORT_STACKS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We were unable to create the transport interface object.
|
|
* The MCS_NO_TRANSPORT_STACKS error is now a fatal error.
|
|
*/
|
|
WARNING_OUT (("Controller::Controller: "
|
|
"failure allocating TCP transport interface"));
|
|
*mcs_error = MCS_NO_TRANSPORT_STACKS;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ~Controller ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This is the destructor for the Controller. It destroys all objects
|
|
* owned by the controller. Note that it attempts to do this in a
|
|
* systematic way to facilitate cleanup shut down. If first deletes
|
|
* connection objects, giving them the opportunity to send disconnects
|
|
* to both the local and remote domains. Then it deletes the transport
|
|
* interfaces. Next it deletes the user objects, giving them the
|
|
* opportunity to cleanly sever their attachments to both the user
|
|
* applications and the local domains. Then it deletes the application
|
|
* interfaces (which should no longer be needed). Lastly it deletes
|
|
* the domains, which should be empty as a result of all user attachments
|
|
* and MCS connections being destroyed.
|
|
*/
|
|
Controller::~Controller ()
|
|
{
|
|
PConnection lpConnection;
|
|
PConnectionPending lpConnectionPending;
|
|
//PTransportInterface lpTransportInterface;
|
|
//PUser lpUser;
|
|
PDomain lpDomain;
|
|
|
|
/*
|
|
* We need to wait for the mutex before destroying the controller. Note
|
|
* that we do not check the return value from the wait because we have to
|
|
* destroy this object no matter what.
|
|
*/
|
|
EnterCriticalSection (&g_MCS_Critical_Section);
|
|
|
|
/*
|
|
* This code clears out the Connection List. Here it necessary to delete
|
|
* not only the connection objects, but also the connection information
|
|
* structure which is maintained by the controller.
|
|
*/
|
|
m_ConnectionList2.Reset();
|
|
while (NULL != (lpConnection = m_ConnectionList2.Iterate()))
|
|
{
|
|
delete lpConnection;
|
|
}
|
|
|
|
Connection_Deletion_Pending = FALSE;
|
|
|
|
/*
|
|
* Clear out the connection pending list. This includes freeing up memory
|
|
* that was allocated to hold the connection pending structure.
|
|
*/
|
|
while (NULL != (lpConnectionPending = m_ConnPendingList2.Get()))
|
|
{
|
|
delete lpConnectionPending;
|
|
}
|
|
|
|
/*
|
|
* This code clears out the Domain List. All domain objects are deleted.
|
|
*/
|
|
while (NULL != (lpDomain = m_DomainList2.Get()))
|
|
{
|
|
delete lpDomain;
|
|
}
|
|
|
|
if (m_fControllerThreadActive)
|
|
{
|
|
/*
|
|
* Set the flag that indicates to the event loop thread that it is time to
|
|
* die. Then, we wait for the thread to terminate itself.
|
|
*/
|
|
Controller_Closing = TRUE;
|
|
|
|
// Give the eventloop a chance to exit
|
|
SetEvent(Connection_Deletion_Pending_Event);
|
|
}
|
|
|
|
// We can now leave MCS's critical section
|
|
LeaveCriticalSection (&g_MCS_Critical_Section);
|
|
|
|
/*
|
|
* If a thread termination event was successfully created for this controller, we must
|
|
* wait on it.
|
|
*/
|
|
if (m_fControllerThreadActive)
|
|
{
|
|
/*
|
|
* If the DLL instance variable is NULL, the process is
|
|
* detaching from the DLL. This is the abnormal termination
|
|
* case (after a GPF, for example). In this case, the event
|
|
* loop thread has already exited, and we should not wait for it.
|
|
*/
|
|
if (g_hDllInst != NULL)
|
|
WaitForSingleObject (Synchronization_Event, INFINITE);
|
|
|
|
CloseHandle (Synchronization_Event);
|
|
//
|
|
// Relinquish the remainder of our time slice, to allow controller thread to exit.
|
|
//
|
|
Sleep(0);
|
|
}
|
|
|
|
// Now, we can delete MCS's critical section
|
|
DeleteCriticalSection (&g_MCS_Critical_Section);
|
|
|
|
// Delete the transport interface and the application interfaces
|
|
delete g_Transport;
|
|
|
|
/*
|
|
* If an event object was successfully allocated for application interface
|
|
* events, then destroy it.
|
|
*/
|
|
if (Transport_Transmit_Event != NULL)
|
|
CloseHandle (Transport_Transmit_Event);
|
|
|
|
/*
|
|
* If an event object was successfully allocated for connection deletion pending
|
|
* events, then destroy it.
|
|
*/
|
|
if (Connection_Deletion_Pending_Event != NULL)
|
|
CloseHandle (Connection_Deletion_Pending_Event);
|
|
|
|
/*
|
|
* If there is a packet coder, then delete it here.
|
|
*/
|
|
delete g_MCSCoder;
|
|
|
|
delete g_CNPCoder;
|
|
// Cleanup the User class
|
|
User::CleanupClass();
|
|
|
|
// Free up the preallocated DataPacket objects.
|
|
DataPacket::FreeMemoryPool ();
|
|
|
|
g_pMCSController = NULL;
|
|
}
|
|
|
|
/*
|
|
* ULong APIENTRY ControllerThread ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
*/
|
|
ULong APIENTRY ControllerThread (
|
|
PVoid controller_ptr)
|
|
{
|
|
//BOOL bTcpOK;
|
|
PController pController = (PController) controller_ptr;
|
|
/*
|
|
* This is the "C" entry point for the controller thread. All it does is
|
|
* use the address passed in to invoke the proper public member function of
|
|
* the object that owns the thread. All real work is done in the C++
|
|
* member function.
|
|
*/
|
|
|
|
/* Set the New Thread's Priority. It's OK if the call fails. */
|
|
SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
|
|
|
pController->CreateTCPWindow ();
|
|
SetEvent (pController->Synchronization_Event);
|
|
|
|
if (TCP_Window_Handle != NULL) {
|
|
// Initialize QoS
|
|
CoInitialize(NULL);
|
|
InitializeQoS();
|
|
pController->EventLoop ();
|
|
|
|
/*
|
|
* Destroy the TCP window. Since we are here, it has been created
|
|
* successfully.
|
|
*/
|
|
pController->DestroyTCPWindow ();
|
|
|
|
// Notify the Controller destructor that the thread exited
|
|
SetEvent (pController->Synchronization_Event);
|
|
|
|
// Cleanup QoS
|
|
DeInitializeQoS();
|
|
CoUninitialize();
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Void CreateTCPWindow ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This method registers the class of the TCP window and creates it.
|
|
*
|
|
* Returns:
|
|
* TRUE, if successful. FALSE, otherwise
|
|
*/
|
|
|
|
Void Controller::CreateTCPWindow ()
|
|
{
|
|
/*
|
|
* Create the window and the resources needed by the transport
|
|
*/
|
|
WNDCLASS window_class = {
|
|
0, /* style */
|
|
WindowProcedure, /* lpfnWndProc */
|
|
0, /* cbClsExtra */
|
|
0, /* cbWndExtra */
|
|
0, /* hInstance */
|
|
NULL, /* hIcon */
|
|
NULL, /* hCursor */
|
|
NULL, /* hbrBackground */
|
|
NULL, /* lpszMenuName */
|
|
MSMCSTCP_WINDOW_CLASS_NAME /* lpszClassName */
|
|
};
|
|
|
|
/* Get the HINSTANCE for this Thread */
|
|
window_class.hInstance = g_hDllInst;
|
|
|
|
/* Register the hidden window's class */
|
|
if(RegisterClass((const WNDCLASS *) (&window_class)) != 0) {
|
|
TCP_Window_Handle = CreateWindow(
|
|
MSMCSTCP_WINDOW_CLASS_NAME, /* address of registered class name */
|
|
MSMCSTCP_WINDOW_CLASS_NAME, /* address of window name */
|
|
WS_POPUP, /* window style */
|
|
CW_USEDEFAULT, /* horizontal position of window */
|
|
CW_USEDEFAULT, /* vertical position of window */
|
|
CW_USEDEFAULT, /* window width */
|
|
CW_USEDEFAULT, /* window height */
|
|
HWND(NULL), /* handle of parent or owner window */
|
|
HMENU(NULL), /* handle of menu */
|
|
g_hDllInst, /* handle of application instance */
|
|
LPVOID(NULL)); /* address of window-creation data */
|
|
|
|
if(TCP_Window_Handle != NULL) {
|
|
#ifndef NO_TCP_TIMER
|
|
/* Create a timer */
|
|
Timer_ID = SetTimer (TCP_Window_Handle, 0,
|
|
(unsigned int) MSMCSTCP_TIMER_DURATION,
|
|
(TIMERPROC) NULL);
|
|
#endif /* NO_TCP_TIMER */
|
|
}
|
|
else {
|
|
WARNING_OUT (( "Controller::CreateTCPWindow: Error Creating %s", MSMCSTCP_WINDOW_CLASS_NAME));
|
|
}
|
|
}
|
|
else {
|
|
WARNING_OUT (( "Controller::CreateTCPWindow: Error Registering %s",MSMCSTCP_WINDOW_CLASS_NAME));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Void DestroyTCPWindow ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This method destroys the TCP window and its class.
|
|
*
|
|
*/
|
|
|
|
Void Controller::DestroyTCPWindow ()
|
|
{
|
|
/*
|
|
* This code clears out the TCP Transport interface.
|
|
*/
|
|
if (TCP_Window_Handle != NULL) {
|
|
TRACE_OUT(("Controller::DestroyTCPWindow: Destroying TCP window..."));
|
|
#ifndef NO_TCP_TIMER
|
|
if (Timer_ID != 0)
|
|
KillTimer (TCP_Window_Handle, Timer_ID);
|
|
#endif /* NO_TCP_TIMER */
|
|
if(DestroyWindow (TCP_Window_Handle) == FALSE)
|
|
{
|
|
WARNING_OUT (("Controller::DestroyTCPWindow: Error Destroying %s", MSMCSTCP_WINDOW_CLASS_NAME));
|
|
}
|
|
|
|
/* Unregister the Window Class */
|
|
if(UnregisterClass(MSMCSTCP_WINDOW_CLASS_NAME, g_hDllInst) == FALSE)
|
|
{
|
|
WARNING_OUT (("Controller::DestroyTCPWindow: Error Unregistering %s", MSMCSTCP_WINDOW_CLASS_NAME));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void EventLoop ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
*/
|
|
Void Controller::EventLoop ()
|
|
{
|
|
HANDLE event_list[NUMBER_OF_EVENTS];
|
|
ULong object_signaled;
|
|
BOOL bFlushMoreData;
|
|
MSG msg;
|
|
BOOL fGCCWork;
|
|
|
|
//
|
|
// Externals from GCC.
|
|
//
|
|
extern HANDLE g_hevGCCOutgoingPDU;
|
|
BOOL GCCRetryFlushOutgoingPDU ( void );
|
|
|
|
/*
|
|
* Set the initial timeout interval to infinite
|
|
*/
|
|
Controller_Wait_Timeout = INFINITE;
|
|
Controller_Event_Mask = 0;
|
|
|
|
/*
|
|
* Set up the event list (this is used in the Wait call below).
|
|
*/
|
|
event_list[TRANSPORT_TRANSMIT_EVENT] = Transport_Transmit_Event;
|
|
event_list[CONNECTION_DELETION_PENDING_EVENT] = Connection_Deletion_Pending_Event;
|
|
event_list[GCC_FLUSH_OUTGOING_PDU_EVENT] = g_hevGCCOutgoingPDU;
|
|
|
|
/*
|
|
* Continue looping until this controller closes down.
|
|
*/
|
|
while (TRUE)
|
|
{
|
|
// Process the TCP window messages.
|
|
while (PeekMessage (&msg, TCP_Window_Handle, 0, 0, PM_REMOVE)) {
|
|
ASSERT (TCP_Window_Handle == msg.hwnd);
|
|
EnterCriticalSection (&g_MCS_Critical_Section);
|
|
DispatchMessage (&msg);
|
|
LeaveCriticalSection (&g_MCS_Critical_Section);
|
|
}
|
|
|
|
/*
|
|
* Go wait for something to happen (or for the timeout to expire,
|
|
* which will cause us to poll for unfinished activity).
|
|
*/
|
|
object_signaled = MsgWaitForMultipleObjects (NUMBER_OF_EVENTS, event_list,
|
|
FALSE, Controller_Wait_Timeout, QS_ALLINPUT);
|
|
|
|
//
|
|
// Default is that no GCC work needs to be done.
|
|
//
|
|
fGCCWork = FALSE;
|
|
|
|
/*
|
|
* Wait for the critical section to be available, before performing
|
|
* any work on the event.
|
|
*/
|
|
EnterCriticalSection (&g_MCS_Critical_Section);
|
|
|
|
if(Controller_Closing) {
|
|
LeaveCriticalSection (&g_MCS_Critical_Section);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Respond to the event dependent on which event occured.
|
|
*/
|
|
switch (object_signaled) {
|
|
case WAIT_TIMEOUT:
|
|
fGCCWork = (Controller_Event_Mask & GCC_FLUSH_OUTGOING_PDU_MASK);
|
|
/*
|
|
* We need to retry an operation.
|
|
*/
|
|
PollMCSDevices ();
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + CONNECTION_DELETION_PENDING_EVENT:
|
|
/*
|
|
* If a Connection object has asked to be deleted, then do it.
|
|
*/
|
|
while (Connection_Deletion_Pending)
|
|
{
|
|
CConnectionList2 Deletion_Pending_Copy(m_ConnectionDeletionList2);
|
|
ConnectionHandle connection_handle;
|
|
PConnection connection;
|
|
|
|
Connection_Deletion_Pending = FALSE;
|
|
while (NULL != (connection = Deletion_Pending_Copy.Get(&connection_handle)))
|
|
{
|
|
/*
|
|
* Get the handle and pointer to the connection object to
|
|
* be deleted. Then remove it from both the connection
|
|
* list, and the connection polling list. Finally, delete
|
|
* the connection object.
|
|
*/
|
|
m_ConnectionList2.Remove(connection_handle);
|
|
m_ConnPollList.Remove(connection);
|
|
delete connection; // This could set the Connection_Deletion_Pending flag to TRUE
|
|
m_ConnectionDeletionList2.Remove(connection_handle);
|
|
}
|
|
if (Connection_Deletion_Pending == FALSE)
|
|
{
|
|
m_ConnectionDeletionList2.Clear();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + TRANSPORT_TRANSMIT_EVENT:
|
|
/*
|
|
* Iterate through the poll list, asking each connection to
|
|
* flush any queued messages.
|
|
*/
|
|
PConnection lpConnection;
|
|
|
|
bFlushMoreData = FALSE;
|
|
Domain_Traffic_Allowed = TRUE;
|
|
m_ConnPollList.Reset();
|
|
while (NULL != (lpConnection = m_ConnPollList.Iterate()))
|
|
{
|
|
if (lpConnection->FlushMessageQueue ()) {
|
|
bFlushMoreData = TRUE;
|
|
|
|
/*
|
|
* We have flushed the connection, but it has more to send to
|
|
* the other end. Normally, we will get an FD_WRITE that allows
|
|
* us to resume sending the queued data and will set this event
|
|
* again to allow more sending. However, there is a special case
|
|
* when the domain traffic is disallowed while a connection is
|
|
* coming up. For this case, the timeout has to be small, and
|
|
* we need to set the Domain_Traffic_Allowed variable to
|
|
* distinguish between the two cases.
|
|
*/
|
|
Domain_Traffic_Allowed &= lpConnection->IsDomainTrafficAllowed();
|
|
}
|
|
}
|
|
UpdateWaitInfo (bFlushMoreData, TRANSPORT_TRANSMIT_INDEX);
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + GCC_FLUSH_OUTGOING_PDU_EVENT:
|
|
fGCCWork = TRUE;
|
|
break;
|
|
}
|
|
|
|
// Leave the MCS critical section
|
|
LeaveCriticalSection (&g_MCS_Critical_Section);
|
|
|
|
//
|
|
// GCC work is done here WITHOUT MCS critical section.
|
|
// The order of critical section in T120 is always GCC in front of MCS.
|
|
// If we enter MCS here and enter GCC later in GCCRetryFlushOutgoingPDU(),
|
|
// then we introduce a potential deadlock.
|
|
//
|
|
if (fGCCWork)
|
|
{
|
|
ASSERT(WAIT_TIMEOUT == object_signaled ||
|
|
(WAIT_OBJECT_0 + GCC_FLUSH_OUTGOING_PDU_EVENT) == object_signaled);
|
|
|
|
bFlushMoreData = GCCRetryFlushOutgoingPDU();
|
|
|
|
UpdateWaitInfo (bFlushMoreData, GCC_FLUSH_OUTGOING_PDU_INDEX);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Controller::UpdateWaitInfo ()
|
|
*
|
|
* Private Function Description
|
|
* This routine updates the instance variables Controller_Wait_Timeout
|
|
* and Controller_Event_Mask after the processing of an event
|
|
* in the EventLoop.
|
|
*
|
|
* Formal Parameters:
|
|
* bMoreData - (i) Flag that informs us whether the msg flush that holds a GCC conference query
|
|
* triggered by the event was complete or left msgs
|
|
* unprocessed
|
|
* EventMask - (i) Tells us which event was processed
|
|
*
|
|
* Return Value
|
|
* None
|
|
*
|
|
* Side Effects
|
|
* Controller_Event_Mask and Controller_Wait_Timeout are updated
|
|
*
|
|
* Caveats
|
|
* None
|
|
*/
|
|
|
|
Void Controller::UpdateWaitInfo (
|
|
BOOL bMoreData,
|
|
unsigned int index)
|
|
{
|
|
if (bMoreData) {
|
|
Controller_Event_Mask |= (0x1 << index);
|
|
}
|
|
else {
|
|
if (0 != Controller_Event_Mask)
|
|
Controller_Event_Mask &= ~(0x1 << index);
|
|
}
|
|
|
|
if (0 == Controller_Event_Mask)
|
|
Controller_Wait_Timeout = INFINITE;
|
|
else if (Controller_Event_Mask & TRANSPORT_MASK) {
|
|
if ((Controller_Event_Mask & TRANSPORT_TRANSMIT_MASK) &&
|
|
(Domain_Traffic_Allowed == FALSE))
|
|
Controller_Wait_Timeout = CONTROLLER_THREAD_TIMEOUT;
|
|
else if (Controller_Event_Mask & TRANSPORT_RECEIVE_MASK)
|
|
Controller_Wait_Timeout = TRANSPORT_RECEIVE_TIMEOUT;
|
|
else
|
|
Controller_Wait_Timeout = TRANSPORT_TRANSMIT_TIMEOUT;
|
|
}
|
|
else
|
|
Controller_Wait_Timeout = CONTROLLER_THREAD_TIMEOUT;
|
|
}
|
|
|
|
/*
|
|
* ULong OwnerCallback ()
|
|
*
|
|
* Public
|
|
*
|
|
* Functional Description:
|
|
* This is the owner callback entry function for the controller. It is
|
|
* through this function that all of the controller's "children" make
|
|
* requests of the controller. Rather than put a lot of otherwise
|
|
* unrelated code in one place, this function merely unpacks the
|
|
* parameters and sends them to a different private member function
|
|
* for each owner callback.
|
|
*
|
|
* Sometimes the parameters are packed directly into the two long
|
|
* parameters, and sometimes one of the parameters is a pointer to a
|
|
* structure that contains more data. This function takes care of that
|
|
* distinction, and passes the appropriate data along to each separate
|
|
* member function.
|
|
*/
|
|
void Controller::HandleTransportDataIndication
|
|
(
|
|
PTransportData pTransport_data
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
TransportDataIndication(pTransport_data->transport_connection,
|
|
pTransport_data->user_data,
|
|
pTransport_data->user_data_length);
|
|
|
|
// We need to free up the transport buffer with the original data.
|
|
FreeMemory(pTransport_data->memory);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
}
|
|
|
|
void Controller::HandleTransportWaitUpdateIndication
|
|
(
|
|
BOOL fMoreData
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
/*
|
|
* We are setting ourselves to wake up again after some time
|
|
* because there has been a read message that could not allocate any
|
|
* buffers.
|
|
*/
|
|
UpdateWaitInfo(fMoreData, TRANSPORT_RECEIVE_INDEX);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
}
|
|
|
|
#ifdef NM_RESET_DEVICE
|
|
MCSError Controller::HandleAppletResetDevice
|
|
(
|
|
PResetDeviceInfo pDevInfo
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
MCSError rc = ApplicationResetDevice(pDevInfo->device_identifier);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
return rc;
|
|
}
|
|
#endif // NM_RESET_DEVICE
|
|
|
|
MCSError Controller::HandleAppletCreateDomain
|
|
(
|
|
GCCConfID *domain_selector
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
MCSError rc = ApplicationCreateDomain(domain_selector);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
return rc;
|
|
}
|
|
|
|
MCSError Controller::HandleAppletDeleteDomain
|
|
(
|
|
GCCConfID *domain_selector
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
MCSError rc = ApplicationDeleteDomain(domain_selector);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
return rc;
|
|
}
|
|
|
|
MCSError Controller::HandleAppletConnectProviderRequest
|
|
(
|
|
PConnectRequestInfo pReqInfo
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
MCSError rc = ApplicationConnectProviderRequest(pReqInfo);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
return rc;
|
|
}
|
|
|
|
MCSError Controller::HandleAppletConnectProviderResponse
|
|
(
|
|
PConnectResponseInfo pRespInfo
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
MCSError rc = ApplicationConnectProviderResponse(
|
|
pRespInfo->connection_handle,
|
|
pRespInfo->domain_selector,
|
|
pRespInfo->domain_parameters,
|
|
pRespInfo->result,
|
|
pRespInfo->user_data,
|
|
pRespInfo->user_data_length);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
return rc;
|
|
}
|
|
|
|
MCSError Controller::HandleAppletDisconnectProviderRequest
|
|
(
|
|
ConnectionHandle hConn
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
MCSError rc = ApplicationDisconnectProviderRequest(hConn);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
return rc;
|
|
}
|
|
|
|
MCSError Controller::HandleAppletAttachUserRequest
|
|
(
|
|
PAttachRequestInfo pReqInfo
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
MCSError rc = ApplicationAttachUserRequest(pReqInfo->domain_selector,
|
|
pReqInfo->ppuser);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void Controller::HandleConnDeleteConnection
|
|
(
|
|
ConnectionHandle hConn
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
ConnectionDeleteConnection(hConn);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
}
|
|
|
|
void Controller::HandleConnConnectProviderConfirm
|
|
(
|
|
PConnectConfirmInfo pConfirmInfo,
|
|
ConnectionHandle hConn
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
ConnectionConnectProviderConfirm(hConn,
|
|
pConfirmInfo->domain_parameters,
|
|
pConfirmInfo->result,
|
|
pConfirmInfo->memory);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
}
|
|
|
|
void Controller::HandleTransportDisconnectIndication
|
|
(
|
|
TransportConnection TrnsprtConn,
|
|
ULONG *pnNotify
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
TransportDisconnectIndication(TrnsprtConn);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
}
|
|
|
|
#ifdef TSTATUS_INDICATION
|
|
void Controller::HandleTransportStatusIndication
|
|
(
|
|
PTransportStatus pStatus
|
|
)
|
|
{
|
|
// Wait for the critical section before attempting any processing
|
|
EnterCriticalSection(&g_MCS_Critical_Section);
|
|
|
|
TransportStatusIndication(pStatus);
|
|
|
|
// Release the critical section
|
|
LeaveCriticalSection(&g_MCS_Critical_Section);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef NM_RESET_DEVICE
|
|
/*
|
|
* ULong ApplicationResetDevice ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function is used to send a reset command to a specified transport
|
|
* stack. MCS performs no processing on this command except to pass it
|
|
* through.
|
|
*
|
|
* Formal Parameters:
|
|
* device_identifier
|
|
* This is an ASCII string that is passed through to the transport
|
|
* stack to effect the reset. It will typically contain information
|
|
* identifying which device within the stack is to be reset.
|
|
*
|
|
* Return Value:
|
|
* MCS_NO_ERROR
|
|
* Everything worked fine.
|
|
* MCS_INVALID_PARAMETER
|
|
* The specified transport stack does not exist.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
ULong Controller::ApplicationResetDevice (
|
|
PChar device_identifier)
|
|
{
|
|
TransportError transport_error;
|
|
MCSError return_value;
|
|
|
|
ASSERT (g_Transport != NULL);
|
|
|
|
/*
|
|
* Forward the reset device command to the transport interface
|
|
* object.
|
|
*/
|
|
TRACE_OUT(("Controller::ApplicationResetDevice: "
|
|
"sending ResetDevice to transport interface"));
|
|
transport_error = g_Transport->ResetDevice (device_identifier);
|
|
|
|
/*
|
|
* Set the return value appropriate for the value returned from the
|
|
* transport call.
|
|
*/
|
|
switch (transport_error)
|
|
{
|
|
case TRANSPORT_ILLEGAL_COMMAND:
|
|
return_value = MCS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
case TRANSPORT_MEMORY_FAILURE:
|
|
return_value = MCS_ALLOCATION_FAILURE;
|
|
break;
|
|
|
|
default:
|
|
return_value = MCS_NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Remove this connection from the connection list
|
|
//
|
|
PConnection connection;
|
|
ConnectionHandle connection_handle;
|
|
|
|
while (NULL != (connection = m_ConnectionList2.Iterate(&connection_handle)))
|
|
{
|
|
if (0 == ::My_strcmpA(connection->GetCalledAddress(), device_identifier))
|
|
{
|
|
PNodeControllerMessage node_controller_message;
|
|
|
|
DBG_SAVE_FILE_LINE
|
|
node_controller_message = new NodeControllerMessage;
|
|
|
|
if (node_controller_message != NULL) {
|
|
/*
|
|
* Fill out the node controller message structure to indicate the
|
|
* disconnect.
|
|
*/
|
|
node_controller_message->message_type =
|
|
MCS_DISCONNECT_PROVIDER_INDICATION;
|
|
node_controller_message->u.disconnect_provider_indication.
|
|
connection_handle = (ConnectionHandle) connection_handle;
|
|
node_controller_message->u.disconnect_provider_indication.
|
|
reason = REASON_DOMAIN_DISCONNECTED;
|
|
node_controller_message->memory = NULL;
|
|
|
|
/*
|
|
* Put the message into the control queue to be sent to the node
|
|
* controller during the next heartbeat.
|
|
*/
|
|
AddToMessageQueue (node_controller_message);
|
|
}
|
|
else
|
|
ERROR_OUT (("Controller::ApplicationResetDevice: "
|
|
"failed to allocate node controller msg"));
|
|
}
|
|
}
|
|
|
|
return ((ULong) return_value);
|
|
}
|
|
#endif //NM_RESET_DEVICE
|
|
|
|
|
|
/*
|
|
* Controller::PollMCSDevices ()
|
|
*
|
|
* Public Function Description
|
|
* This is the MCS controller's heartbeat. It will call the heartbeat
|
|
* equivalent functions for the Application SAPs, the connections and
|
|
* the users.
|
|
*/
|
|
Void Controller::PollMCSDevices()
|
|
{
|
|
BOOL bFlushMoreData;
|
|
|
|
if (Controller_Event_Mask & TRANSPORT_TRANSMIT_MASK) {
|
|
/*
|
|
* Iterate through the poll list, asking each connection to
|
|
* flush any queued messages.
|
|
*/
|
|
PConnection lpConnection;
|
|
|
|
bFlushMoreData = FALSE;
|
|
Domain_Traffic_Allowed = TRUE;
|
|
m_ConnPollList.Reset();
|
|
while (NULL != (lpConnection = m_ConnPollList.Iterate()))
|
|
{
|
|
if (lpConnection->FlushMessageQueue ()) {
|
|
bFlushMoreData = TRUE;
|
|
|
|
/*
|
|
* We have flushed the connection, but it has more to send to
|
|
* the other end. Normally, we will get an FD_WRITE that allows
|
|
* us to resume sending the queued data and will set this event
|
|
* again to allow more sending. However, there is a special case
|
|
* when the domain traffic is disallowed while a connection is
|
|
* coming up. For this case, the timeout has to be small, and
|
|
* we need to set the Domain_Traffic_Allowed variable to
|
|
* distinguish between the two cases.
|
|
*/
|
|
Domain_Traffic_Allowed &= lpConnection->IsDomainTrafficAllowed();
|
|
}
|
|
}
|
|
UpdateWaitInfo (bFlushMoreData, TRANSPORT_TRANSMIT_INDEX);
|
|
}
|
|
|
|
if (Controller_Event_Mask & TRANSPORT_RECEIVE_MASK) {
|
|
ASSERT (g_Transport);
|
|
g_Transport->ReceiveBufferAvailable();
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* MCSError ApplicationCreateDomain ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This request comes through one of the application interface objects.
|
|
* the only parameter is a domain selector. If a domain with that
|
|
* selector does not currently exist, it will be created.
|
|
*
|
|
* A domain must be created before user attachments or MCS connections can
|
|
* be created.
|
|
*
|
|
* Formal Parameters:
|
|
* domain_selector (i)
|
|
* This is the domain selector for the new domain.
|
|
* domain_selector_length (i)
|
|
* This is the length of the above domain selector.
|
|
*
|
|
* Return Value:
|
|
* MCS_NO_ERROR
|
|
* The domain was successfully created.
|
|
* MCS_ALLOCATION_FAILURE
|
|
* A memory allocation failure prevented the successful creation of
|
|
* the new domain.
|
|
* MCS_DOMAIN_ALREADY_EXISTS
|
|
* The named domain already exists.
|
|
*
|
|
* Side Effects:
|
|
* A logical domain now exists that can accomodate both user attachments
|
|
* and MCS connections.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
MCSError Controller::ApplicationCreateDomain
|
|
(
|
|
GCCConfID *domain_selector
|
|
)
|
|
{
|
|
PDomain domain;
|
|
MCSError return_value;
|
|
|
|
/*
|
|
* Check to see if the requested domain already exists. If so, then
|
|
* do not attempt to create a new one. Just return the appropriate
|
|
* return value.
|
|
*/
|
|
if (m_DomainList2.Find(*domain_selector) == FALSE)
|
|
{
|
|
/*
|
|
* The next action is to instantiate a new domain object. This
|
|
* is initially an empty domain that will be associated with the
|
|
* user provided domain selector.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
domain = new Domain ();
|
|
if (domain != NULL)
|
|
{
|
|
/*
|
|
* If everything was allocated successfully, then simply put
|
|
* the new domain into the domain list dictionary. and set the
|
|
* return value to indicate success.
|
|
*/
|
|
TRACE_OUT (("Controller::ApplicationCreateDomain: "
|
|
"domain creation successful"));
|
|
m_DomainList2.Append(*domain_selector, domain);
|
|
return_value = MCS_NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Set the return value to indication a memory allocation failure.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationCreateDomain: "
|
|
"domain creation failed"));
|
|
return_value = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The domain was not created since it already exists.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationCreateDomain: "
|
|
"domain already exists"));
|
|
return_value = MCS_DOMAIN_ALREADY_EXISTS;
|
|
}
|
|
|
|
return (return_value);
|
|
}
|
|
|
|
/*
|
|
* MCSError ApplicationDeleteDomain ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This request comes from one of the application interface objects. It
|
|
* instructs the controller to delete an existing domain, with the only
|
|
* parameter identifying the domain to be deleted. If the domain exists,
|
|
* then it will be destroyed.
|
|
*
|
|
* Note that all user attachments and MCS connections that are attached
|
|
* to the domain when it is deleted will also be deleted (automatically).
|
|
*
|
|
* Formal Parameters:
|
|
* domain_selector (i)
|
|
* This is the domain selector for the domain to be deleted.
|
|
* domain_selector_length (i)
|
|
* This is the length of the above domain selector.
|
|
*
|
|
* Return Value:
|
|
* MCS_NO_ERROR
|
|
* The domain was successfully deleted.
|
|
* MCS_NO_SUCH_DOMAIN
|
|
* There is no domain associated with the passed in domain selector.
|
|
*
|
|
* Side Effects:
|
|
* When the domain is deleted, all resources used by it (including user
|
|
* attachments and MCS connections) will be deleted as well.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
MCSError Controller::ApplicationDeleteDomain
|
|
(
|
|
GCCConfID *domain_selector
|
|
)
|
|
{
|
|
PDomain domain;
|
|
MCSError return_value;
|
|
|
|
/*
|
|
* Check to see if the domain selector refers to a valid domain.
|
|
*/
|
|
if (NULL != (domain = m_DomainList2.Remove(*domain_selector)))
|
|
{
|
|
/*
|
|
* If the domain selector is valid, then remove the domain from the
|
|
* dictionary and delete it. Everything else happens automatically
|
|
* as a result of destroying the domain object.
|
|
*/
|
|
TRACE_OUT (("Controller::ApplicationDeleteDomain: deleting domain"));
|
|
delete domain;
|
|
return_value = MCS_NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the domain selector is not in the dictionary, then report the
|
|
* error to the caller.
|
|
*/
|
|
ERROR_OUT (("Controller::ApplicationDeleteDomain: invalid domain"));
|
|
return_value = MCS_NO_SUCH_DOMAIN;
|
|
}
|
|
|
|
return (return_value);
|
|
}
|
|
|
|
/*
|
|
* MCSError ApplicationConnectProviderRequest ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This request originates from one of the application interface objects.
|
|
* This happens as the result of the node controller issuing a
|
|
* ConnectProviderRequest to whichever application interface object
|
|
* that it is attached to. If the parameters are valid, then a new
|
|
* connection object will be created to represent the outbound connection.
|
|
* This will result in the connection object calling the proper transport
|
|
* interface to create the transport connection, etc.
|
|
*
|
|
* Formal Parameters:
|
|
* pcriConnectRequestInfo (i)
|
|
* Contains all the needed info to complete the Connect Provider Request.
|
|
*
|
|
* Return Value:
|
|
* MCS_NO_ERROR
|
|
* The request was successful. The connection handle for the newly
|
|
* created connection has been stored at the address passed into this
|
|
* request (see parameter list above). Note that this connection
|
|
* handle can be used to destroy the new connection immediately, even
|
|
* if the physical connection has not yet been established. This
|
|
* allows the node controller to abort a dial in-progress by calling
|
|
* DisconnectProviderRequest.
|
|
* MCS_INVALID_PARAMETER
|
|
* The format of the called address field is incorrect.
|
|
* MCS_ALLOCATION_FAILURE
|
|
* The request was unable to complete successfully due to a memory
|
|
* allocation failure (either in MCS or the transport layer).
|
|
* MCS_TRANSPORT_NOT_READY
|
|
* The transport layer could not process the request because it is not
|
|
* ready. This usually means that initialization has not successfully
|
|
* completed.
|
|
* MCS_DOMAIN_NOT_HIERARCHICAL
|
|
* This request is attempting to create an upward connection to a
|
|
* domain that already has an upward connection (which is not valid).
|
|
* MCS_NO_SUCH_DOMAIN
|
|
* The specified local domain does not exist within this provider.
|
|
*
|
|
* Side Effects:
|
|
* An outbound connect establishment process is begun.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
MCSError Controller::ApplicationConnectProviderRequest (
|
|
PConnectRequestInfo pcriConnectRequestInfo)
|
|
{
|
|
PDomain domain;
|
|
BOOL bTransportIdFound;
|
|
PConnection connection;
|
|
MCSError return_value;
|
|
PChar called_address = pcriConnectRequestInfo->called_address;
|
|
PConnectionHandle connection_handle = pcriConnectRequestInfo->connection_handle;
|
|
|
|
/*
|
|
* Make sure the local domain specified corresponds to an existing
|
|
* domain.
|
|
*/
|
|
ASSERT(sizeof(GCCConfID) == sizeof(*(pcriConnectRequestInfo->calling_domain)));
|
|
if (NULL != (domain = m_DomainList2.Find(*(pcriConnectRequestInfo->calling_domain))))
|
|
{
|
|
/*
|
|
* Check to make sure that the requested connection is valid.
|
|
* Specifically, make sure that this is not a request for an upward
|
|
* connection to a domain that already has an upward connection.
|
|
* This would result in a non-hierarchical domain, which is illegal.
|
|
*/
|
|
if ((pcriConnectRequestInfo->upward_connection == FALSE) || (domain->IsTopProvider ()))
|
|
{
|
|
PChar pColon;
|
|
/*
|
|
* Look for the colon that separates the identifier from the
|
|
* address.
|
|
*/
|
|
|
|
for (bTransportIdFound = FALSE, pColon = called_address; *pColon; pColon++)
|
|
if (*pColon == ':') {
|
|
bTransportIdFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Make sure that there was a colon before continuing.
|
|
*/
|
|
if (bTransportIdFound)
|
|
{
|
|
ASSERT (g_Transport != NULL);
|
|
|
|
called_address = pColon + 1;
|
|
/*
|
|
* Allocate an unused connection handle to be
|
|
* associated with the new MCS connection.
|
|
*/
|
|
*connection_handle = AllocateConnectionHandle ();
|
|
|
|
/*
|
|
* Create a new connection object. The constructor
|
|
* parameters provide everything that the connection
|
|
* object will need to create a new outbound MCS
|
|
* connection.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
connection = new Connection (domain,
|
|
*connection_handle,
|
|
pcriConnectRequestInfo->calling_domain,
|
|
pcriConnectRequestInfo->called_domain,
|
|
called_address,
|
|
pcriConnectRequestInfo->fSecure,
|
|
pcriConnectRequestInfo->upward_connection,
|
|
pcriConnectRequestInfo->domain_parameters,
|
|
pcriConnectRequestInfo->user_data,
|
|
pcriConnectRequestInfo->user_data_length,
|
|
&return_value);
|
|
/*
|
|
* Check to see if the allocation of the connection
|
|
* worked.
|
|
*/
|
|
if (connection != NULL)
|
|
{
|
|
/*
|
|
* Even if the connection object was allocated
|
|
* successfully, it is still possible that an error
|
|
* occurred while it was trying to initialize. So
|
|
* check the return value from the contructor.
|
|
*/
|
|
if (return_value == MCS_NO_ERROR)
|
|
{
|
|
/*
|
|
* Put the connection into the connection list
|
|
* dictionary.
|
|
*/
|
|
TRACE_OUT (("Controller::ApplicationConnectProviderRequest: "
|
|
"new connection created"));
|
|
m_ConnectionList2.Insert(*connection_handle, connection);
|
|
m_ConnPollList.Append(connection);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the connection object was successfully
|
|
* allocated, but its initialization failed,
|
|
* then it is necessary to destroy the faulty
|
|
* connection and return the appropriate error
|
|
* to the caller.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationConnectProviderRequest: "
|
|
"deleting faulty connection"));
|
|
delete connection;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The allocation of the connection object has
|
|
* failed. Simply return the appropriate error and
|
|
* abort the request.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationConnectProviderRequest: "
|
|
"connection allocation failed"));
|
|
return_value = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
|
|
// Put back the colon in the "called_address"
|
|
*pColon = ':';
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* There was not a colon in the called address, so MCS has
|
|
* no way of extracting the transport identifier. The request
|
|
* must therefore fail.
|
|
*/
|
|
ERROR_OUT (("Controller::ApplicationConnectProviderRequest: "
|
|
"no colon in called address"));
|
|
return_value = MCS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The domain already has an upward connection (or one pending).
|
|
* This request is therefore invalid and must be rejected.
|
|
*/
|
|
ERROR_OUT (("Controller::ApplicationConnectProviderRequest: "
|
|
"domain not hierarchical"));
|
|
return_value = MCS_DOMAIN_NOT_HIERARCHICAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the local domain selector does not correspond to a valid
|
|
* domain in this provider, then fail the request immediately by
|
|
* returning the appropriate error.
|
|
*/
|
|
ERROR_OUT (("Controller::ApplicationConnectProviderRequest: "
|
|
"invalid local domain"));
|
|
return_value = MCS_NO_SUCH_DOMAIN;
|
|
}
|
|
|
|
return (return_value);
|
|
}
|
|
|
|
/*
|
|
* MCSError ApplicationConnectProviderResponse ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This request originates from one of the application interface objects.
|
|
* This happens as the result of the node controller issuing a
|
|
* ConnectProviderResponse to whichever application interface object
|
|
* that it is attached to. If the connection handle is valid, and the
|
|
* local domain still exists, then that domain will be told whether or not
|
|
* the incoming connection was accepted. This allows it to repsond
|
|
* appropriately.
|
|
*
|
|
* Formal Parameters:
|
|
* connection_handle (i)
|
|
* This identifies the connection from which a previous connect
|
|
* provider indication originated. This request essentially states
|
|
* whether or not this incoming connection is accepted.
|
|
* domain_selector (i)
|
|
* This is the domain selector of the domain that the node controller
|
|
* wishes to bind the incoming connection to.
|
|
* domain_selector_length (i)
|
|
* This is the length of the above domain selector.
|
|
* domain_parameters (i)
|
|
* This is a pointer to a structure containing the domain parameters
|
|
* that the node controller wishes to use for this connection.
|
|
* result (i)
|
|
* This is the result to be sent to the remote provider. Coming
|
|
* from the node controller this should be either RESULT_SUCCESSFUL
|
|
* or RESULT_USER_REJECTED. If it is anything but RESULT_SUCCESSFUL,
|
|
* the associated connection will be immediately destroyed.
|
|
* user_data (i)
|
|
* This is the address of the user data that is to be sent in the
|
|
* connect response PDU to the remote provider.
|
|
* user_data_length (i)
|
|
* This is the length of the user data to be sent in the connect
|
|
* response PDU to the remote provider.
|
|
*
|
|
* Return Value:
|
|
* MCS_NO_ERROR
|
|
* The response was sent to the appropriate domain successfully.
|
|
* MCS_DOMAIN_PARAMETERS_UNACCEPTABLE
|
|
* This indicates that there is no overlap in the min and max
|
|
* parameters specified by the remote node, and the min and max
|
|
* parameters acceptable to the specified domain. Therefore, no
|
|
* connection is possible. This does NOT indicate that there is
|
|
* anything wrong with the specified target parameters (which are
|
|
* just suggested values anyway).
|
|
* MCS_ALLOCATION_FAILURE
|
|
* The response failed due to a memory allocation failure.
|
|
* MCS_NO_SUCH_DOMAIN
|
|
* This indicates that the domain associated with the pending
|
|
* response has been deleted since the indication was sent.
|
|
* MCS_NO_SUCH_CONNECTION
|
|
* This indicates that the connection has been lost since the
|
|
* indication was issued.
|
|
* MCS_DOMAIN_NOT_HIERARCHICAL
|
|
* This request is attempting to create an upward connection to a
|
|
* domain that already has an upward connection (which is not valid).
|
|
*
|
|
* Side Effects:
|
|
* If the response is other than RESULT_SUCCESSFUL, the transport
|
|
* connection that conveys the connect response PDU will be severed.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
MCSError Controller::ApplicationConnectProviderResponse (
|
|
ConnectionHandle connection_handle,
|
|
GCCConfID *domain_selector,
|
|
PDomainParameters domain_parameters,
|
|
Result result,
|
|
PUChar user_data,
|
|
ULong user_data_length)
|
|
{
|
|
PConnectionPending connection_pending;
|
|
TransportConnection transport_connection;
|
|
BOOL upward_connection;
|
|
PDomain domain;
|
|
PConnection connection;
|
|
MCSError return_value;
|
|
|
|
/*
|
|
* Check to see if the connection handle corresponds to a connection
|
|
* that is awaiting a response.
|
|
*/
|
|
if (NULL != (connection_pending = m_ConnPendingList2.Find(connection_handle)))
|
|
{
|
|
/*
|
|
* Get the address of the structure containing information about the
|
|
* pending connection. Then load the contained information into
|
|
* automatic variables for easier manipulation.
|
|
*/
|
|
transport_connection = connection_pending->transport_connection;
|
|
upward_connection = connection_pending->upward_connection;
|
|
if (domain_parameters == NULL)
|
|
domain_parameters = &connection_pending->domain_parameters;
|
|
|
|
/*
|
|
* See if the node controller has elected to accept or reject the
|
|
* incoming connection. If it is accepted, then the response must
|
|
* be sent through the appropriate domain object. If it is
|
|
* rejected, then the response can be sent directly to the
|
|
* connection object (which will then delete itself).
|
|
*/
|
|
if (result == RESULT_SUCCESSFUL)
|
|
{
|
|
/*
|
|
* See if the specified domain is valid, before trying to send
|
|
* the response to it.
|
|
*/
|
|
if (NULL != (domain = m_DomainList2.Find(*domain_selector)))
|
|
{
|
|
/*
|
|
* Check to make sure that the requested connection is valid.
|
|
* Specifically, make sure that this is not a request for an
|
|
* upward connection to a domain that already has an upward
|
|
* connection. This would result in a non-hierarchical domain,
|
|
* which is illegal.
|
|
*/
|
|
if ((upward_connection == FALSE) || (domain->IsTopProvider ()))
|
|
{
|
|
/*
|
|
* Create the connection object that will be responsible
|
|
* for the inbound connection. It will automatically issue
|
|
* a ConnectResponse during construction.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
connection = new Connection (domain,
|
|
connection_handle, transport_connection,
|
|
upward_connection, domain_parameters,
|
|
&connection_pending->minimum_domain_parameters,
|
|
&connection_pending->maximum_domain_parameters,
|
|
user_data, user_data_length,
|
|
&return_value);
|
|
|
|
if (connection != NULL)
|
|
{
|
|
if (return_value == MCS_NO_ERROR)
|
|
{
|
|
/*
|
|
* Everything worked fine. Remove the connection
|
|
* handle from the pending list, and put the newly
|
|
* created connection object into the active
|
|
* connection list.
|
|
*/
|
|
TRACE_OUT(("Controller::ApplicationConnectProviderResponse: "
|
|
"connection created successfully"));
|
|
m_ConnPendingList2.Remove(connection_handle);
|
|
delete connection_pending;
|
|
m_ConnectionList2.Insert(connection_handle, connection);
|
|
m_ConnPollList.Append(connection);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The contructor failed, which probably indicates
|
|
* an allocation failure. Report this to the node
|
|
* controller, and delete the faulty connection
|
|
* object.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationConnectProviderResponse: "
|
|
"connection constructor failed"));
|
|
delete connection;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The allocation failed. Report this to the node
|
|
* controller.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationConnectProviderResponse: "
|
|
"connection constructor failed"));
|
|
return_value = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The domain already has an upward connection (or one
|
|
* pending). This request is therefore invalid and must be
|
|
* rejected.
|
|
*/
|
|
ERROR_OUT (("Controller::ApplicationConnectProviderResponse:"
|
|
" domain not hierarchical"));
|
|
return_value = MCS_DOMAIN_NOT_HIERARCHICAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the indicated domain is not valid, then simply return
|
|
* the appropriate error.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationConnectProviderResponse: "
|
|
"invalid domain"));
|
|
return_value = MCS_NO_SUCH_DOMAIN;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The node controller has elected to reject the incoming
|
|
* connection. It is therefore not necessary to create a
|
|
* connection object. Send the connect response directly to
|
|
* the transport interface object, and then disconnect the
|
|
* transport connection.
|
|
*/
|
|
TRACE_OUT (("Controller::ApplicationConnectProviderResponse: connection rejected"));
|
|
|
|
ASSERT (g_Transport);
|
|
ConnectResponse (transport_connection, result,
|
|
domain_parameters, 0, user_data, user_data_length);
|
|
|
|
g_Transport->DisconnectRequest (transport_connection);
|
|
|
|
/*
|
|
* Remove the connection handle from the pending list, and
|
|
* delete the structure that was holding information about
|
|
* the pending connection.
|
|
*/
|
|
m_ConnPendingList2.Remove(connection_handle);
|
|
delete connection_pending;
|
|
return_value = MCS_NO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the connection handle is no longer valid, then fail the request
|
|
* with the appropriate error.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationConnectProviderResponse: "
|
|
"invalid connection"));
|
|
return_value = MCS_NO_SUCH_CONNECTION;
|
|
}
|
|
|
|
return (return_value);
|
|
}
|
|
|
|
/*
|
|
* MCSError ApplicationDisconnectProviderRequest ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This request originates from one of the application interface objects.
|
|
* This happens as the result of the node controller issuing a
|
|
* DisconnectProviderRequest to whichever application interface object
|
|
* that it is attached to. If the connection handle is valid, then the
|
|
* connection object wil be destroyed, which will break the transport
|
|
* connections associated with it.
|
|
*
|
|
* Formal Parameters:
|
|
* connection_handle (i)
|
|
* This identifies the connection to be destroyed.
|
|
*
|
|
* Return Value:
|
|
* MCS_NO_ERROR
|
|
* The named connection has been successfully deleted.
|
|
* MCS_NO_SUCH_CONNECTION
|
|
* The connection handle is invalid.
|
|
*
|
|
* Side Effects:
|
|
* One or more transport connections will be broken. Furthermore, if
|
|
* this is an upward connection for a domain, then the domain itself
|
|
* will be eradicated (all attachments and connections will be severed).
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
MCSError Controller::ApplicationDisconnectProviderRequest (
|
|
ConnectionHandle connection_handle)
|
|
{
|
|
MCSError return_value;
|
|
PConnection connection;
|
|
PConnectionPending connection_pending;
|
|
|
|
/*
|
|
* Check to see if the connection handle refers to an existing connection.
|
|
*/
|
|
if (NULL != (connection = m_ConnectionList2.Find(connection_handle)))
|
|
{
|
|
/*
|
|
* If the connection handle is valid, then delete the associated
|
|
* connection and remove it from the connection dictionary. It is also
|
|
* necessary to delete the connection information structure.
|
|
*/
|
|
TRACE_OUT (("Controller::ApplicationDisconnectProviderRequest: "
|
|
"deleting connection"));
|
|
m_ConnectionList2.Remove(connection_handle);
|
|
m_ConnPollList.Remove(connection);
|
|
delete connection;
|
|
|
|
/*
|
|
* Check to see if this connection handle is also in the connection
|
|
* deletion list. If so, then remove it from there as well.
|
|
*/
|
|
m_ConnectionDeletionList2.Remove(connection_handle);
|
|
|
|
return_value = MCS_NO_ERROR;
|
|
}
|
|
|
|
else if (NULL != (connection_pending = m_ConnPendingList2.Remove(connection_handle)))
|
|
{
|
|
/*
|
|
* This connection handle refers to a connection that is still
|
|
* pending. Delete it from there.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationDisconnectProviderRequest: "
|
|
"deleting pending connection"));
|
|
delete connection_pending;
|
|
|
|
return_value = MCS_NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the connection handle is not in either of the above dictionaries,
|
|
* then return the appropriate error.
|
|
*/
|
|
TRACE_OUT (("Controller::ApplicationDisconnectProviderRequest: "
|
|
"invalid connection"));
|
|
return_value = MCS_NO_SUCH_CONNECTION;
|
|
}
|
|
|
|
return (return_value);
|
|
}
|
|
|
|
/*
|
|
* MCSError ApplicationAttachUserRequest ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function is used to attach a user application to an existing
|
|
* domain. The user handle that is returned can then be used by the
|
|
* application to request services from MCS.
|
|
*
|
|
* After verifying that the specified domain really does exist, the
|
|
* controller will create a new user object. The new user object will
|
|
* attach itself to both the domain and the application interface
|
|
* specified by the controller. At that point, information can flow
|
|
* through the application interface to the user and then on to the
|
|
* domain without having to pass through the controller.
|
|
*
|
|
* Formal Parameters:
|
|
* domain_selector (i)
|
|
* This identifies the domain to which the user wants to attach.
|
|
* domain_selector_length (i)
|
|
* This is the length of the above domain selector.
|
|
* attachment_flags (i)
|
|
* This is a set of flags that allow the user application to control
|
|
* how the attachment is handled. The only flag currently used by
|
|
* the controller specifies whether or not the user wants to receive
|
|
* callbacks during the controller's heartbeat.
|
|
* ppUser (o)
|
|
* This is a pointer to a user handle, which will be set to a valid
|
|
* value by the controller if this function completes successfully.
|
|
* The user handle is really a pointer to a User object.
|
|
*
|
|
* Return Value:
|
|
* MCS_NO_ERROR
|
|
* Everything completed successfully. Note that the attachment
|
|
* cannot actually be used by the user application until it has
|
|
* received a successful attach user confirm from the domain to
|
|
* which it has attached. This return value merely indicates that
|
|
* process was started successfully.
|
|
* MCS_ALLOCATION_FAILURE
|
|
* This attach request was unable to successfully complete due to a
|
|
* memory allocation failure.
|
|
* MCS_NO_SUCH_DOMAIN
|
|
* This attach request was unable to successfully complete because
|
|
* the specified domain does not exist within this provider.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
MCSError Controller::ApplicationAttachUserRequest
|
|
(
|
|
GCCConfID *domain_selector,
|
|
PUser *ppUser
|
|
)
|
|
{
|
|
MCSError return_value;
|
|
PDomain domain;
|
|
|
|
/*
|
|
* First of all make sure that the domain selector refers to a valid
|
|
* domain. If it doesn't, then return the appropriate error.
|
|
*/
|
|
if (NULL != (domain = m_DomainList2.Find(*domain_selector)))
|
|
{
|
|
/*
|
|
* Instantiate a new user object, with the domain and the application
|
|
* interface pointer as parameters.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
*ppUser = (PUser) new User (domain, &return_value);
|
|
|
|
/*
|
|
* Make sure the allocation completed successfully
|
|
*/
|
|
if (*ppUser != NULL) {
|
|
/*
|
|
* The creation of the user object was successful.
|
|
*/
|
|
if (return_value != MCS_NO_ERROR) {
|
|
// We have to cleanup the object.
|
|
(*ppUser)->Release();
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* There was a memory allocation failure, so return the
|
|
* appropriate error.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationAttachUserRequest: "
|
|
"user creation failed"));
|
|
return_value = MCS_ALLOCATION_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The specified domain does not exist, so return the appropriate
|
|
* error.
|
|
*/
|
|
WARNING_OUT (("Controller::ApplicationAttachUserRequest: invalid domain"));
|
|
return_value = MCS_NO_SUCH_DOMAIN;
|
|
}
|
|
|
|
return (return_value);
|
|
}
|
|
|
|
/*
|
|
* Void ConnectionDeleteConnection ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This request originates within a connection object when it determines
|
|
* the need to delete itself. This is usually caused by one of three
|
|
* things. First, the connection was rejected (inbound or outbound).
|
|
* Second, either the local or remote domain issued a disconnect
|
|
* provider ultimatum. Or third, a transport connection was unexpectedly
|
|
* lost.
|
|
*
|
|
* The controller responds by deleting the connection, after the
|
|
* parameters are validated. It also issues a disconnect provider
|
|
* indication to the node controller.
|
|
*
|
|
* Formal Parameters:
|
|
* connection_handle (i)
|
|
* This is the handle of the connection object that wishes to be
|
|
* deleted.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* An MCS connection is terminated, which may result in the destruction
|
|
* of one or more transport connections.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
Void Controller::ConnectionDeleteConnection (
|
|
ConnectionHandle connection_handle)
|
|
{
|
|
PConnection connection;
|
|
|
|
/*
|
|
* Make sure the connection handle is in the dictionary before proceeding.
|
|
*/
|
|
if (NULL != (connection = m_ConnectionList2.Find(connection_handle)))
|
|
{
|
|
/*
|
|
* See if the deletion of this connection is already pending. If so,
|
|
* there is no need to queue it up again.
|
|
*/
|
|
if (! m_ConnectionDeletionList2.Find(connection_handle))
|
|
{
|
|
/*
|
|
* Put the connection object into the deletion list and set the
|
|
* deletion pending flag.
|
|
*/
|
|
TRACE_OUT (("Controller::ConnectionDeleteConnection: "
|
|
"adding connection to deletion list"));
|
|
m_ConnectionDeletionList2.Insert(connection_handle, connection);
|
|
Connection_Deletion_Pending = TRUE;
|
|
SetEvent(Connection_Deletion_Pending_Event);
|
|
|
|
/*
|
|
* Post the message to the controller window (GCC and MCS
|
|
* use the same window to post messages to their controllers).
|
|
*/
|
|
if (! PostMessage (g_pControlSap->GetHwnd(),
|
|
MCTRLMSG_BASE + MCS_DISCONNECT_PROVIDER_INDICATION,
|
|
NULL, (LPARAM) connection_handle)) {
|
|
ERROR_OUT(("Controller::ConnectionDeleteConnection: "
|
|
"failed to post msg to MCS controller window. Error: %d", GetLastError()));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the connection handle cannot be found in the connection
|
|
* dictionary, then simply ignore the request.
|
|
*/
|
|
WARNING_OUT (("Controller::ConnectionDeleteConnection: "
|
|
"unknown connection"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ConnectionConnectProviderConfirm ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This request originates within a domain object upon reception of a
|
|
* connect response PDU. The controller responds by sending a connect
|
|
* provider confirm to the node controller.
|
|
*
|
|
* Formal Parameters:
|
|
* connection_handle (i)
|
|
* This is the handle of the connection object from which the connect
|
|
* provider confirm was received.
|
|
* domain_parameters (i)
|
|
* This is a pointer to a structure that contains the domain parameters
|
|
* that were decided on during capabilities arbitration.
|
|
* result (i)
|
|
* This contains the result of the connect request. Anything but
|
|
* RESULT_SUCCESSFUL means that the connection was rejected.
|
|
* memory (i)
|
|
* If this is not NULL, it contains the user data that was received
|
|
* in the connect response PDU.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* A connect provider confirm is sent to the node controller.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
void Controller::ConnectionConnectProviderConfirm (
|
|
ConnectionHandle connection_handle,
|
|
PDomainParameters domain_parameters,
|
|
Result result,
|
|
PMemory memory)
|
|
{
|
|
PConnection connection;
|
|
PUChar user_data;
|
|
ULong user_data_length;
|
|
ConnectProviderConfirm *pconnect_provider_confirm;
|
|
|
|
/*
|
|
* Make sure the connection handle is in the dictionary before proceeding.
|
|
*/
|
|
if (NULL != (connection = m_ConnectionList2.Find(connection_handle)))
|
|
{
|
|
// Allocate the node controller msg.
|
|
DBG_SAVE_FILE_LINE
|
|
pconnect_provider_confirm = new ConnectProviderConfirm;
|
|
|
|
if (pconnect_provider_confirm != NULL) {
|
|
/*
|
|
* Check to see if there is user data associated with this confirm.
|
|
*/
|
|
if (memory != NULL)
|
|
{
|
|
/*
|
|
* If there is user data, lock it, and get the address and length
|
|
* into temporary variables.
|
|
*/
|
|
LockMemory (memory);
|
|
user_data = memory->GetPointer ();
|
|
user_data_length = memory->GetLength ();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If there is no user data, then set the address and length fields
|
|
* to default values.
|
|
*/
|
|
user_data = NULL;
|
|
user_data_length = 0;
|
|
}
|
|
|
|
/*
|
|
* Put all information about this confirm into the node controller
|
|
* message structure allocated above.
|
|
*/
|
|
pconnect_provider_confirm->connection_handle = (ConnectionHandle) connection_handle;
|
|
pconnect_provider_confirm->domain_parameters = *domain_parameters;
|
|
pconnect_provider_confirm->result = result;
|
|
pconnect_provider_confirm->user_data = user_data;
|
|
pconnect_provider_confirm->user_data_length = user_data_length;
|
|
pconnect_provider_confirm->pb_cred = NULL;
|
|
pconnect_provider_confirm->cb_cred = 0;
|
|
|
|
DWORD cb = 0;
|
|
if (GetSecurityInfo(connection_handle, NULL, &cb))
|
|
{
|
|
if (cb > 0 && NOT_DIRECTLY_CONNECTED != cb)
|
|
{
|
|
pconnect_provider_confirm->pb_cred = (PBYTE) CoTaskMemAlloc(cb);
|
|
if (NULL != pconnect_provider_confirm->pb_cred)
|
|
{
|
|
if (GetSecurityInfo(connection_handle, pconnect_provider_confirm->pb_cred, &cb))
|
|
{
|
|
pconnect_provider_confirm->cb_cred = cb;
|
|
}
|
|
else
|
|
{
|
|
CoTaskMemFree(pconnect_provider_confirm->pb_cred);
|
|
pconnect_provider_confirm->pb_cred = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("Controller::ConnectionConnectProviderConfirm: Memory Allocation Error"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Post the message to the controller window (GCC and MCS
|
|
* use the same window to post messages to their controllers).
|
|
*/
|
|
if (! PostMessage (g_pControlSap->GetHwnd(),
|
|
MCTRLMSG_BASE + MCS_CONNECT_PROVIDER_CONFIRM,
|
|
(WPARAM) memory, (LPARAM) pconnect_provider_confirm)) {
|
|
ERROR_OUT(("Controller::ConnectionDeleteConnection: "
|
|
"failed to post msg to MCS controller window. Error: %d", GetLastError()));
|
|
}
|
|
|
|
/*
|
|
* If the result of this confirm is not successful, and the connection
|
|
* is not already queued for deletion, then we need to queue it for
|
|
* deletion.
|
|
*/
|
|
if ((result != RESULT_SUCCESSFUL) &&
|
|
(! m_ConnectionDeletionList2.Find(connection_handle)))
|
|
{
|
|
/*
|
|
* Put the connection object into the deletion list and set the
|
|
* deletion pending flag.
|
|
*/
|
|
TRACE_OUT (("Controller::ConnectionConnectProviderConfirm: "
|
|
"adding connection to deletion list"));
|
|
m_ConnectionDeletionList2.Insert(connection_handle, connection);
|
|
Connection_Deletion_Pending = TRUE;
|
|
SetEvent(Connection_Deletion_Pending_Event);
|
|
}
|
|
}
|
|
else
|
|
ERROR_OUT(("Controller::ConnectionConnectProviderConfirm: "
|
|
"failed to allocate node controller msg."));
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the connection handle cannot be found in the connection
|
|
* dictionary, then simply ignore the request.
|
|
*/
|
|
WARNING_OUT (("Controller::ConnectionConnectProviderConfirm: "
|
|
"unknown connection"));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Void TransportDisconnectIndication ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This request originates within a transport interface object when it
|
|
* gets a disconnect indication from the transport layer for a transport
|
|
* connection that is not assigned to a connection object. This could
|
|
* happen in the case where a remote node issues a connect provider request
|
|
* followed by a disconnect provider request before this node issues a
|
|
* connect provider response.
|
|
*
|
|
* The controller responds by simply removing the information from the
|
|
* connection pending list.
|
|
*
|
|
* Formal Parameters:
|
|
* transport_connection (i)
|
|
* This is the transport connection handle that has been assigned to
|
|
* the newly created transport connection.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
Void Controller::TransportDisconnectIndication (
|
|
TransportConnection transport_connection)
|
|
{
|
|
|
|
PConnectionPending connection_pending;
|
|
ConnectionHandle connection_handle;
|
|
|
|
/*
|
|
* Find the entry in the connection pending list which is associated with
|
|
* the given transport connection. If found, remove the entry.
|
|
*/
|
|
m_ConnPendingList2.Reset();
|
|
while (NULL != (connection_pending = m_ConnPendingList2.Iterate(&connection_handle)))
|
|
{
|
|
if (IS_SAME_TRANSPORT_CONNECTION(connection_pending->transport_connection, transport_connection))
|
|
{
|
|
m_ConnPendingList2.Remove(connection_handle);
|
|
delete connection_pending;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void TransportDataIndication ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This function is called when data is received from the transport layer
|
|
* on a transport connection that no other object has registered
|
|
* ownership of.
|
|
*
|
|
* Formal Parameters:
|
|
* transport_connection (i)
|
|
* This is the transport connection handle that has been assigned to
|
|
* the newly created transport connection.
|
|
* user_data
|
|
* A pointer to the data received.
|
|
* user_data_length
|
|
* The length of the data received.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
Void Controller::TransportDataIndication (
|
|
TransportConnection transport_connection,
|
|
PUChar user_data,
|
|
ULong user_data_length)
|
|
{
|
|
PPacket packet;
|
|
PacketError packet_error;
|
|
PVoid pdu_structure;
|
|
|
|
ASSERT (g_Transport);
|
|
/*
|
|
* Create a packet from the encoded data received from the transport
|
|
* interface. Retrieve the decoded PDU structure from the packet and
|
|
* pass it on to the appropriate processing routine.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
packet = new Packet (
|
|
(PPacketCoder) g_MCSCoder,
|
|
BASIC_ENCODING_RULES,
|
|
user_data + PROTOCOL_OVERHEAD_X224,
|
|
user_data_length - PROTOCOL_OVERHEAD_X224,
|
|
CONNECT_MCS_PDU,
|
|
TRUE,
|
|
&packet_error);
|
|
|
|
if (packet != NULL)
|
|
{
|
|
if (packet_error == PACKET_NO_ERROR)
|
|
{
|
|
/*
|
|
* Get a pointer to the decoded data.
|
|
*/
|
|
pdu_structure = packet->GetDecodedData ();
|
|
|
|
switch (((ConnectMCSPDU *) pdu_structure)->choice)
|
|
{
|
|
case CONNECT_INITIAL_CHOSEN:
|
|
ProcessConnectInitial (
|
|
transport_connection,
|
|
&((ConnectMCSPDU *) pdu_structure)->u.
|
|
connect_initial);
|
|
break;
|
|
|
|
case CONNECT_ADDITIONAL_CHOSEN:
|
|
ProcessConnectAdditional (
|
|
transport_connection,
|
|
&((ConnectMCSPDU *) pdu_structure)->u.
|
|
connect_additional);
|
|
break;
|
|
|
|
default:
|
|
ERROR_OUT (("Controller::TransportDataIndication: "
|
|
"received invalid PDU (%d)",
|
|
((ConnectMCSPDU *) pdu_structure)->choice));
|
|
g_Transport->DisconnectRequest (transport_connection);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* A memory allocation failure has prevented us from processing
|
|
* this PDU. Destroy the connection that carried it.
|
|
*/
|
|
WARNING_OUT (("Controller::TransportDataIndication: "
|
|
"packet constructor failed"));
|
|
g_Transport->DisconnectRequest (transport_connection);
|
|
}
|
|
packet->Unlock ();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* A memory allocation failure has prevented us from processing
|
|
* this PDU. Destroy the connection that carried it.
|
|
*/
|
|
WARNING_OUT (("Controller::TransportDataIndication: "
|
|
"packet allocation failed"));
|
|
g_Transport->DisconnectRequest (transport_connection);
|
|
}
|
|
}
|
|
|
|
#ifdef TSTATUS_INDICATION
|
|
/*
|
|
* Void TransportStatusIndication ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This request originates within a transport interface object when it
|
|
* receives a status indication from its transport layer. This function
|
|
* will forward the status indication to the node controller.
|
|
*
|
|
* Formal Parameters:
|
|
* transport_status
|
|
* This is a pointer to the TransportStatus structure that describes
|
|
* the reason for the indication.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
Void Controller::TransportStatusIndication (
|
|
PTransportStatus transport_status)
|
|
{
|
|
ULong device_identifier_length;
|
|
ULong remote_address_length;
|
|
ULong message_length;
|
|
PMemory memory;
|
|
PUChar string_address;
|
|
PNodeControllerMessage node_controller_message;
|
|
|
|
/*
|
|
* Determine the length of each of the ASCII strings contained in the
|
|
* transport status indications. This will be used to allocate a
|
|
* memory block large enough to hold them all. Note that each length
|
|
* includes one extra byte to hold the ASCII NULL terminator.
|
|
*/
|
|
device_identifier_length =
|
|
(ULong) lstrlen (transport_status->device_identifier) + 1;
|
|
remote_address_length =
|
|
(ULong) lstrlen (transport_status->remote_address) + 1;
|
|
message_length =
|
|
(ULong) lstrlen (transport_status->message) + 1;
|
|
|
|
/*
|
|
* Use the memory manager to allocate a memory block large enough to
|
|
* hold all of the strings.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
memory = AllocateMemory (NULL,
|
|
(device_identifier_length + remote_address_length + message_length));
|
|
|
|
if (memory != NULL)
|
|
{
|
|
DBG_SAVE_FILE_LINE
|
|
node_controller_message = new NodeControllerMessage;
|
|
|
|
if (node_controller_message != NULL) {
|
|
/*
|
|
* Get the address of the memory block that was allocated.
|
|
*/
|
|
string_address = memory->GetPointer ();
|
|
|
|
/*
|
|
* Indicate what type of message this is.
|
|
*/
|
|
node_controller_message->message_type = MCS_TRANSPORT_STATUS_INDICATION;
|
|
|
|
/*
|
|
* Copy all of the ASCII strings into the memory block that was
|
|
* allocated above. This block will remain valid until this
|
|
* message is sent to the node controller.
|
|
*/
|
|
memcpy (string_address, transport_status->device_identifier,
|
|
device_identifier_length);
|
|
node_controller_message->u.transport_status_indication.
|
|
device_identifier = (PChar) string_address;
|
|
string_address += (Int) device_identifier_length;
|
|
|
|
memcpy (string_address, transport_status->remote_address,
|
|
remote_address_length);
|
|
node_controller_message->u.transport_status_indication.
|
|
remote_address = (PChar) string_address;
|
|
string_address += (Int) remote_address_length;
|
|
|
|
memcpy (string_address, transport_status->message,
|
|
message_length);
|
|
node_controller_message->u.transport_status_indication.
|
|
message = (PChar) string_address;
|
|
|
|
node_controller_message->u.transport_status_indication.
|
|
state = transport_status->state;
|
|
|
|
node_controller_message->memory = memory;
|
|
|
|
/*
|
|
* Put this message into the control queue to be sent to the node
|
|
* controller during the next heartbeat.
|
|
*/
|
|
AddToMessageQueue (node_controller_message);
|
|
}
|
|
else
|
|
WARNING_OUT(("Controller::TransportStatusIndication: "
|
|
"WARNING - memory allocation failure"));
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* A memory allocation failure has occurred. This prevents us from
|
|
* being able to deliver this status indication to the node controller.
|
|
* This does not compromise the integrity of MCS, but could cause
|
|
* problems at a higher level.
|
|
*/
|
|
ERROR_OUT (("Controller::TransportStatusIndication: "
|
|
"WARNING - memory allocation failure"));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Void ProcessConnectInitial()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* Processes incoming connect initial PDUs. Sends a connect provider
|
|
* indication to the node controller if everything checks out.
|
|
*
|
|
* Formal Parameters:
|
|
* transport_connection (i)
|
|
* This is assigned transport connection handle for the connection
|
|
* that carried the PDU.
|
|
* pdu_structure (i)
|
|
* This is a pointer to the PDU itself.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
void Controller::ProcessConnectInitial (
|
|
TransportConnection transport_connection,
|
|
ConnectInitialPDU * pdu_structure)
|
|
{
|
|
PConnectionPending connection_pending;
|
|
PMemory memory;
|
|
PUChar memory_address;
|
|
ConnectProviderIndication *pconnect_provider_indication;
|
|
ConnectionHandle connection_handle;
|
|
BOOL upward_connection;
|
|
//DomainParameters domain_parameters;
|
|
|
|
/*
|
|
* Try to allocate a connection pending structure. This will be used to
|
|
* hold information about the incoming connection that will not be passed
|
|
* back in the connect provider response.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
connection_pending = new ConnectionPending;
|
|
DBG_SAVE_FILE_LINE
|
|
pconnect_provider_indication = new ConnectProviderIndication;
|
|
|
|
if (connection_pending != NULL && pconnect_provider_indication != NULL)
|
|
{
|
|
/*
|
|
* Allocate a memory block to hold the user data field in the
|
|
* incoming connection.
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
memory = AllocateMemory (NULL, pdu_structure->user_data.length);
|
|
|
|
if (memory != NULL) {
|
|
|
|
memory_address = memory->GetPointer ();
|
|
|
|
/*
|
|
* Allocate a connection handle for this inbound connection,
|
|
* and put it into the indication structure. Also fill in the
|
|
* physical connection handle, which is obtained by asking the
|
|
* transport interface for it.
|
|
*/
|
|
connection_handle = AllocateConnectionHandle ();
|
|
pconnect_provider_indication->connection_handle = connection_handle;
|
|
pconnect_provider_indication->fSecure =
|
|
g_Transport->GetSecurity ( transport_connection );
|
|
|
|
/*
|
|
* Copy the user data field into the
|
|
* newly allocated memory block. Also set the pointers in
|
|
* the node controller message structure to point into the
|
|
* memory block.
|
|
*/
|
|
memcpy (memory_address,
|
|
pdu_structure->user_data.value,
|
|
pdu_structure->user_data.length);
|
|
pconnect_provider_indication->user_data = memory_address;
|
|
pconnect_provider_indication->user_data_length =
|
|
pdu_structure->user_data.length;
|
|
|
|
/*
|
|
* Retrieve the direction of the incoming connection. Put it
|
|
* into both the connect provider indication structure and the
|
|
* connection pending structure. Note that in the connection
|
|
* pending structure, we need to reverse the direction of the
|
|
* flag so that it is from the point-of-view of this provider.
|
|
*/
|
|
upward_connection = pdu_structure->upward_flag;
|
|
pconnect_provider_indication->upward_connection = upward_connection;
|
|
|
|
if (upward_connection)
|
|
connection_pending->upward_connection = FALSE;
|
|
else
|
|
connection_pending->upward_connection = TRUE;
|
|
|
|
/*
|
|
* Retrieve the target domain parameters and put them into both
|
|
* the connect provider indication structure, and into the
|
|
* connection pending structure (for possible later use).
|
|
*/
|
|
memcpy (&(pconnect_provider_indication->domain_parameters),
|
|
&(pdu_structure->target_parameters), sizeof (PDUDomainParameters));
|
|
memcpy (&(connection_pending->domain_parameters),
|
|
&(pdu_structure->target_parameters), sizeof (PDUDomainParameters));
|
|
|
|
/*
|
|
* Retrieve the minimum domain parameters and put them into
|
|
* the connection pending structure (for possible later use).
|
|
*/
|
|
memcpy (&(connection_pending->minimum_domain_parameters),
|
|
&(pdu_structure->minimum_parameters), sizeof(PDUDomainParameters));
|
|
|
|
/*
|
|
* Retrieve the maximum domain parameters and put them into
|
|
* the connection pending structure (for possible later use).
|
|
*/
|
|
memcpy (&(connection_pending->maximum_domain_parameters),
|
|
&(pdu_structure->maximum_parameters), sizeof(PDUDomainParameters));
|
|
|
|
/*
|
|
* Post the message to the controller window (GCC and MCS
|
|
* use the same window to post messages to their controllers).
|
|
*/
|
|
if (NULL != g_pControlSap) {
|
|
if (! PostMessage (g_pControlSap->GetHwnd(),
|
|
MCTRLMSG_BASE + MCS_CONNECT_PROVIDER_INDICATION,
|
|
(WPARAM) memory, (LPARAM) pconnect_provider_indication)) {
|
|
ERROR_OUT(("Controller::ProcessConnectInitial: "
|
|
"failed to post msg to MCS controller window. Error: %d", GetLastError()));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We also need to remember which transport interface and
|
|
* transport connection are associated with this pending
|
|
* MCS connection. Then put the connection pending structure
|
|
* into a list for later use.
|
|
*/
|
|
connection_pending->transport_connection = transport_connection;
|
|
m_ConnPendingList2.Append(connection_handle, connection_pending);
|
|
|
|
// No errors have occurred.
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* A memory allocation failure has occurred. We have no choice
|
|
* but to terminate the connection upon which this PDU arrived.
|
|
*/
|
|
ASSERT (g_Transport);
|
|
WARNING_OUT(("Controller::ProcessConnectInitial: memory allocation failure"));
|
|
delete connection_pending;
|
|
delete pconnect_provider_indication;
|
|
g_Transport->DisconnectRequest (transport_connection);
|
|
}
|
|
|
|
/*
|
|
* Void ProcessConnectAdditional ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* Processes incoming connect additional PDUs. If the connection handle
|
|
* contained therein is valid, it will bind the connection to the
|
|
* proper connection object.
|
|
*
|
|
* Formal Parameters:
|
|
* transport_connection (i)
|
|
* This is assigned transport connection handle for the connection
|
|
* that carried the PDU.
|
|
* pdu_structure (i)
|
|
* This is a pointer to the PDU itself.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* Caveats:
|
|
* None.
|
|
*/
|
|
Void Controller::ProcessConnectAdditional (
|
|
TransportConnection transport_connection,
|
|
ConnectAdditionalPDU * pdu_structure)
|
|
{
|
|
ConnectionHandle connection_handle;
|
|
Priority priority;
|
|
PConnection connection;
|
|
|
|
ASSERT (g_Transport);
|
|
|
|
connection_handle = (ConnectionHandle) pdu_structure->called_connect_id;
|
|
priority = (Priority) pdu_structure->data_priority;
|
|
|
|
if (NULL != (connection = m_ConnectionList2.Find(connection_handle)))
|
|
{
|
|
/*
|
|
* The indicated connection does exist, so call upon it to accept
|
|
* and register the new transport connection.
|
|
*/
|
|
connection->RegisterTransportConnection (transport_connection, priority);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The indicated connection handle is not in the dictionary. Issue
|
|
* a connect result with a failure result, and disconnect the
|
|
* transport connection.
|
|
*/
|
|
ConnectResult (transport_connection, RESULT_UNSPECIFIED_FAILURE);
|
|
|
|
g_Transport->DisconnectRequest (transport_connection);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ConnectResponse ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* Sends a failed connect response PDU (when something goes wrong).
|
|
*
|
|
* Formal Parameters:
|
|
* transport_connection (i)
|
|
* This is assigned transport connection handle for the connection
|
|
* that is to carry the PDU.
|
|
* result (i)
|
|
* This is the result being sent in the connect response.
|
|
* domain_parameters (i)
|
|
* This is a pointer to a structure containing domain parameters.
|
|
* These parameters will not be used for anything since the connection
|
|
* is being rejected.
|
|
* connect_id (i)
|
|
* This is the connect ID that would be used for any additional
|
|
* transport connection to be bound to this one. This is not
|
|
* required since the connection is being rejected.
|
|
* user_data (i)
|
|
* This is a pointer to the user data to be transmitted to the remote
|
|
* side along with the response.
|
|
* user_data_lengthn (i)
|
|
* This is the length of the above user data.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* Caveats:
|
|
*/
|
|
Void Controller::ConnectResponse (
|
|
TransportConnection transport_connection,
|
|
Result result,
|
|
PDomainParameters domain_parameters,
|
|
ConnectID connect_id,
|
|
PUChar user_data,
|
|
ULong user_data_length)
|
|
{
|
|
ConnectMCSPDU connect_response_pdu;
|
|
PPacket packet;
|
|
PacketError packet_error;
|
|
//PVoid encoded_data;
|
|
//ULong encoded_data_length;
|
|
|
|
ASSERT (g_Transport);
|
|
/*
|
|
* Fill in the ConnectResponse PDU structure to be encoded.
|
|
*/
|
|
connect_response_pdu.choice = CONNECT_RESPONSE_CHOSEN;
|
|
connect_response_pdu.u.connect_response.result = (PDUResult)result;
|
|
connect_response_pdu.u.connect_response.called_connect_id = connect_id;
|
|
|
|
memcpy (&(connect_response_pdu.u.connect_response.domain_parameters),
|
|
domain_parameters, sizeof(PDUDomainParameters));
|
|
|
|
connect_response_pdu.u.connect_response.user_data.length = user_data_length;
|
|
connect_response_pdu.u.connect_response.user_data.value = user_data;
|
|
|
|
/*
|
|
* Create a packet which will be used to hold the data to be sent
|
|
* through the transport interface. Check to make sure the packet is
|
|
* successfully created..
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
packet = new Packet (
|
|
(PPacketCoder) g_MCSCoder,
|
|
BASIC_ENCODING_RULES,
|
|
&connect_response_pdu,
|
|
CONNECT_MCS_PDU,
|
|
TRUE,
|
|
&packet_error);
|
|
|
|
if (packet != NULL)
|
|
{
|
|
if (packet_error == PACKET_NO_ERROR)
|
|
{
|
|
/*
|
|
* Send the packet through the transport interface.
|
|
*/
|
|
#ifdef DEBUG
|
|
TransportError err = DataRequest (transport_connection,
|
|
(PSimplePacket) packet);
|
|
ASSERT (err == TRANSPORT_NO_ERROR);
|
|
#else // DEBUG
|
|
DataRequest (transport_connection, (PSimplePacket) packet);
|
|
#endif // DEBUG
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The packet creation has failed due to an internal error so
|
|
* report the error through a print statement. Note that no
|
|
* further action need be taken since this transport connection
|
|
* is being terminated anyway.
|
|
*/
|
|
WARNING_OUT (("Controller::ConnectResponse: "
|
|
"internal allocation failure"));
|
|
}
|
|
packet->Unlock ();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The packet creation has failed so report the error through a print
|
|
* statement. Note that no further action need be taken since this
|
|
* transport connection is being terminated anyway.
|
|
*/
|
|
WARNING_OUT (("Controller::ConnectResponse: "
|
|
"packet allocation failure"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Void ConnectResult ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* Sends a failed connect response PDU (when something goes wrong).
|
|
*
|
|
* Formal Parameters:
|
|
* transport_connection (i)
|
|
* This is assigned transport connection handle for the connection
|
|
* that is to carry the PDU.
|
|
* result (i)
|
|
* This is the result being sent in the connect result.
|
|
*
|
|
* Return Value:
|
|
* None.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* Caveats:
|
|
*/
|
|
Void Controller::ConnectResult (
|
|
TransportConnection transport_connection,
|
|
Result result)
|
|
{
|
|
ConnectMCSPDU connect_result_pdu;
|
|
PPacket packet;
|
|
PacketError packet_error;
|
|
//PVoid encoded_data;
|
|
//ULong encoded_data_length;
|
|
|
|
ASSERT (g_Transport);
|
|
/*
|
|
* Fill in the PDU structure to be encoded.
|
|
*/
|
|
connect_result_pdu.choice = CONNECT_RESULT_CHOSEN;
|
|
connect_result_pdu.u.connect_result.result = (PDUResult)result;
|
|
|
|
/*
|
|
* Create a packet which will be used to hold the data to be sent
|
|
* through the transport interface. Check to make sure the packet is
|
|
* successfully created..
|
|
*/
|
|
DBG_SAVE_FILE_LINE
|
|
packet = new Packet (
|
|
(PPacketCoder) g_MCSCoder,
|
|
BASIC_ENCODING_RULES,
|
|
&connect_result_pdu,
|
|
CONNECT_MCS_PDU,
|
|
TRUE,
|
|
&packet_error);
|
|
|
|
if (packet != NULL)
|
|
{
|
|
if (packet_error == PACKET_NO_ERROR)
|
|
{
|
|
/*
|
|
* Send the packet through the transport interface.
|
|
*/
|
|
#ifdef DEBUG
|
|
TransportError err = DataRequest (transport_connection,
|
|
(PSimplePacket) packet);
|
|
ASSERT (err == TRANSPORT_NO_ERROR);
|
|
#else // DEBUG
|
|
DataRequest (transport_connection, (PSimplePacket) packet);
|
|
#endif // DEBUG
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The packet creation has failed due to an internal error so
|
|
* report the error through a print statement. Note that no
|
|
* further action need be taken since this transport connection
|
|
* is being terminated anyway.
|
|
*/
|
|
WARNING_OUT (("Controller::ConnectResult: "
|
|
"internal allocation failure"));
|
|
}
|
|
packet->Unlock ();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* The packet creation has failed so report the error through a print
|
|
* statement. Note that no further action need be taken since this
|
|
* transport connection is being terminated anyway.
|
|
*/
|
|
WARNING_OUT (("Controller::ConnectResult: "
|
|
"packet allocation failure"));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ConnectionHandle AllocateConnectionHandle ()
|
|
*
|
|
* Private
|
|
*
|
|
* Functional Description:
|
|
* This routine allocates a unique connection handle to be used for a newly
|
|
* created connection object. It is based on a rolling instance variable,
|
|
* so that no two handles will ever be reused until the number rolls
|
|
* over at 0xffff.
|
|
*
|
|
* Note that 0 is not a valid connection handle, and will never be used.
|
|
*
|
|
* Formal Parameters:
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
* The unique connection handle.
|
|
*
|
|
* Side Effects:
|
|
* None.
|
|
*
|
|
* Caveats:
|
|
* Note that the assumption is made that there will never be more than
|
|
* 65,534 handles in use at once. In other words, this loop assumes that
|
|
* there is at least 1 available handle left. If there is not, then the
|
|
* loop will hang forever (this is a pretty safe bet for now).
|
|
*/
|
|
ConnectionHandle Controller::AllocateConnectionHandle ()
|
|
{
|
|
/*
|
|
* This loop simply increments a rolling number, looking for the next
|
|
* one that is not already in use.
|
|
*/
|
|
while (1)
|
|
{
|
|
Connection_Handle_Counter++;
|
|
|
|
/*
|
|
* 0 is not a valid handle, so skip it.
|
|
*/
|
|
if (Connection_Handle_Counter == 0)
|
|
continue;
|
|
|
|
/*
|
|
* If this handle is not in use, break from the loop and use it.
|
|
*/
|
|
if (! m_ConnectionList2.Find(Connection_Handle_Counter))
|
|
break;
|
|
}
|
|
|
|
return (Connection_Handle_Counter);
|
|
}
|
|
|
|
BOOL Controller::GetLocalAddress(ConnectionHandle connection_handle,
|
|
TransportAddress local_address,
|
|
PInt local_address_length)
|
|
{
|
|
PConnection connection = NULL;
|
|
PConnectionPending connection_pending = NULL;
|
|
TransportError transport_error;
|
|
BOOL return_value = FALSE;
|
|
|
|
if (NULL == (connection = m_ConnectionList2.Find(connection_handle)))
|
|
{
|
|
connection_pending = m_ConnPendingList2.Find(connection_handle);
|
|
}
|
|
|
|
if(connection || connection_pending)
|
|
{
|
|
// Ask the local address to the transport interface
|
|
if (connection)
|
|
{
|
|
transport_error = ::GetLocalAddress(connection->GetTransportConnection(TOP_PRIORITY),
|
|
local_address,
|
|
local_address_length);
|
|
}
|
|
else
|
|
{
|
|
transport_error = ::GetLocalAddress(connection_pending->transport_connection,
|
|
local_address,
|
|
local_address_length);
|
|
}
|
|
|
|
// Check the error code
|
|
if (TRANSPORT_NO_ERROR == transport_error) {
|
|
return_value = TRUE;
|
|
}
|
|
}
|
|
|
|
return(return_value);
|
|
}
|
|
|
|
BOOL Controller::FindSocketNumber(ConnectionHandle connection_handle, SOCKET * socket_number)
|
|
{
|
|
PConnection connection = NULL;
|
|
PConnectionPending connection_pending = NULL;
|
|
|
|
if (NULL != (connection = m_ConnectionList2.Find(connection_handle)))
|
|
{
|
|
TransportConnection XprtConn = connection->GetTransportConnection(TOP_PRIORITY);
|
|
if (IS_SOCKET(XprtConn))
|
|
{
|
|
* socket_number = XprtConn.nLogicalHandle;
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
if (NULL != (connection_pending = m_ConnPendingList2.Find(connection_handle)))
|
|
{
|
|
if (IS_SOCKET(connection_pending->transport_connection))
|
|
{
|
|
* socket_number = connection_pending->transport_connection.nLogicalHandle;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|