#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 "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. */ if (g_hMCSThread) { CloseHandle(g_hMCSThread); g_hMCSThread = NULL; } } 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) { CoInitialize(NULL); 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); 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; }