/*++ Copyright (c) 1995 Microsoft Corporation Module Name: net\routing\ipx\sap\serverdb.c Abstract: This module implements SAP Server Table and corresponding API Author: Vadim Eydelman 05-15-1995 Revision History: --*/ #include "sapp.h" // The table SERVER_TABLE ServerTable; // Max number of unsorted servers ULONG SDBMaxUnsortedServers=SAP_SDB_MAX_UNSORTED_DEF; // Interval with which to update the sorted list ULONG SDBSortLatency=SAP_SDB_SORT_LATENCY_DEF; // Size of heap reserved for the database ULONG SDBMaxHeapSize=SAP_SDB_MAX_HEAP_SIZE_DEF; // Local prototypes BOOL AcquireAllLocks ( void ); VOID ReleaseAllLocks ( void ); PSERVER_NODE CreateNode ( IN PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN DWORD Protocol, IN PUCHAR AdvertisingNode, IN PSDB_HASH_LIST HashList ); VOID ChangeMainNode ( IN PSERVER_NODE oldNode, IN PSERVER_NODE newNode, IN PLIST_ENTRY serverLink ); VOID DeleteNode ( IN PSERVER_NODE node ); VOID DeleteMainNode ( IN PSERVER_NODE node ); DWORD DoFindNextNode ( IN PLIST_ENTRY cur, IN PPROTECTED_LIST list, IN INT link, IN DWORD ExclusionFlags, IN OUT PIPX_SERVER_ENTRY_P Server, IN OUT PULONG InterfaceIndex OPTIONAL, IN OUT PULONG Protocol OPTIONAL, OUT PULONG ObjectID OPTIONAL ); VOID DoUpdateSortedList ( void ); PLIST_ENTRY FindIntfLink ( ULONG InterfaceIndex ); PLIST_ENTRY FindTypeLink ( USHORT Type ); PLIST_ENTRY FindSortedLink ( USHORT Type, PUCHAR Name ); INT HashFunction ( PUCHAR Name ); ULONG GenerateUniqueID ( PSDB_HASH_LIST HashList ); /*++ ******************************************************************* C r e a t e S e r v e r T a b l e Routine Description: Allocates resources for server table management Arguments: UpdateObject - this object will be signalled when 'slow' sorted list of servers needs to be updated (UpdateSortedList should be called) TimerObject - this object will be signalled when server expiration queue requires processing (ProcessExpirationQueue should be called) Return Value: NO_ERROR - resources were allocated successfully other - reason of failure (windows error code) ******************************************************************* --*/ DWORD CreateServerTable ( HANDLE *UpdateObject, HANDLE *TimerObject ) { DWORD status=NO_ERROR; INT i; BOOL res; // Use private heap for server entries // to eliminate fragmentation ServerTable.ST_Heap = HeapCreate (0, 0, SDBMaxHeapSize*1024*1024); if (ServerTable.ST_Heap!=NULL) { ServerTable.ST_UpdateTimer = CreateWaitableTimer ( NULL, TRUE, // Manual reset NULL); if (ServerTable.ST_UpdateTimer!=NULL) { *UpdateObject = ServerTable.ST_UpdateTimer; ServerTable.ST_ExpirationTimer = CreateWaitableTimer ( NULL, TRUE, // Manual reset NULL); if (ServerTable.ST_ExpirationTimer!=NULL) { LONGLONG timeout = 0; *TimerObject = ServerTable.ST_ExpirationTimer; ServerTable.ST_LastEnumerator = NULL; ServerTable.ST_ServerCnt = 0; ServerTable.ST_StaticServerCnt = 0; ServerTable.ST_TMPListCnt = 0; ServerTable.ST_DeletedListCnt = 0; ServerTable.ST_UpdatePending = -1; InitializeSyncObjPool (&ServerTable.ST_SyncPool); InitializeProtectedList (&ServerTable.ST_SortedListPRM); InitializeProtectedList (&ServerTable.ST_SortedListTMP); InitializeProtectedList (&ServerTable.ST_DeletedList); InitializeProtectedList (&ServerTable.ST_TypeList); InitializeProtectedList (&ServerTable.ST_IntfList); InitializeProtectedList (&ServerTable.ST_ExpirationQueue); InitializeProtectedList (&ServerTable.ST_ChangedSvrsQueue); for (i=0; i0) { while (ServerTable.ST_UpdatePending!=-1) Sleep (100); } CloseHandle (ServerTable.ST_ExpirationTimer); CloseHandle (ServerTable.ST_UpdateTimer); DeleteProtectedList (&ServerTable.ST_SortedListPRM); DeleteProtectedList (&ServerTable.ST_SortedListTMP); DeleteProtectedList (&ServerTable.ST_DeletedList); DeleteProtectedList (&ServerTable.ST_TypeList); DeleteProtectedList (&ServerTable.ST_IntfList); DeleteProtectedList (&ServerTable.ST_ExpirationQueue); DeleteProtectedList (&ServerTable.ST_ChangedSvrsQueue); for (i=0; iName[0]==0) { Trace (DEBUG_SERVERDB, "Illigal server name in UpdateServer."); return ERROR_INVALID_PARAMETER; } if ( Server-> HopCount > IPX_MAX_HOP_COUNT ) { Trace( DEBUG_SERVERDB, "\tUpdateServer : Invalid Hop count" "type: %04x, hops: %d, name: %.48s.\n", Server->Type, Server->HopCount, Server->Name ); ASSERTERR( FALSE ); return ERROR_INVALID_PARAMETER; } //else //{ // Trace( DEBUG_SERVERDB, "\tUpdateServer : Hop count ok\n" ); //} if (ARGUMENT_PRESENT(NewServer)) *NewServer = TRUE; // First try to locate server in hash list HashList = &ServerTable.ST_HashLists[HashFunction (Server->Name)]; if (!AcquireServerTableList (&HashList->HL_List, TRUE)) return ERROR_GEN_FAILURE; cur = HashList->HL_List.PL_Head.Flink; while (cur!=&HashList->HL_List.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[SDB_HASH_TABLE_LINK]); VALIDATE_NODE(node); if (!IsEnumerator (node)) { if (Server->Type == node->SN_Server.Type) { res = IpxNameCmp (Server->Name, node->SN_Server.Name); if (res==0) { if (ARGUMENT_PRESENT(NewServer)) *NewServer = FALSE; // Loop through all entries in the table for // this server do { // If there is another entry with same interface, // remember its position in interface list // so new entry can be inserted quickly if // necessary if (InterfaceIndex==node->SN_InterfaceIndex) intfLink = &node->N_Links[SDB_INTF_LIST_LINK]; if ((InterfaceIndex==node->SN_InterfaceIndex) && (Protocol == node->SN_Protocol) && (IpxNodeCmp (AdvertisingNode, &node->SN_AdvertisingNode)==0)) { theNode = node; // Exact match if (((Flags & SDB_DISABLED_NODE_FLAG) < (node->N_NodeFlags & SDB_DISABLED_NODE_FLAG)) || (((Flags & SDB_DISABLED_NODE_FLAG) == (node->N_NodeFlags & SDB_DISABLED_NODE_FLAG)) && (Server->HopCount<=node->SN_HopCount))) { // Hop count is better, than the that // of the rest, ignore them if (serverLink==NULL) serverLink = &theNode->SN_ServerLink; break; } } else { // Get the best entry besides the one // we are updating if (mainNode==NULL) mainNode = node; // Find the place for added/updated entry // in the list of entries for this server // (this list is ordered by hop count) if ((serverLink==NULL) && (((Flags & SDB_DISABLED_NODE_FLAG) < (node->N_NodeFlags & SDB_DISABLED_NODE_FLAG)) || (((Flags & SDB_DISABLED_NODE_FLAG) == (node->N_NodeFlags & SDB_DISABLED_NODE_FLAG)) && (Server->HopCount<=node->SN_HopCount)))) { serverLink = &node->SN_ServerLink; // We saw the server and know where // to place it, break out if (theNode!=NULL) break; } } node = CONTAINING_RECORD (node->SN_ServerLink.Flink, SERVER_NODE, SN_ServerLink); VALIDATE_SERVER_NODE(node); } // Loop until we are back to best entry while (!IsMainNode (node)); } else if (res<0) // No chance to see the server: the hash is ordered // be type.name break; } else if (Server->TypeSN_Server.Type) break; } cur = cur->Flink; } if (theNode!=NULL) { if ((IpxNetCmp (theNode->SN_Server.Network, Server->Network)!=0) || (IpxNodeCmp (theNode->SN_Server.Node, Server->Node)!=0) || (IpxSockCmp (theNode->SN_Server.Socket, Server->Socket)!=0) ) { Trace (DEBUG_FAILURES, "Address change for server %.4x %.48s:\n" " Old - %.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x:%.2x%.2x\n" " New - %.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x:%.2x%.2x", Server->Type, Server->Name, theNode->SN_Server.Network[0], theNode->SN_Server.Network[1], theNode->SN_Server.Network[2], theNode->SN_Server.Network[3], theNode->SN_Server.Node[0], theNode->SN_Server.Node[1], theNode->SN_Server.Node[2], theNode->SN_Server.Node[3], theNode->SN_Server.Node[4], theNode->SN_Server.Node[5], theNode->SN_Server.Socket[0], theNode->SN_Server.Socket[1], Server->Network[0], Server->Network[1], Server->Network[2], Server->Network[3], Server->Node[0], Server->Node[1], Server->Node[2], Server->Node[3], Server->Node[4], Server->Node[5], Server->Socket[0], Server->Socket[1] ); IF_LOG (EVENTLOG_WARNING_TYPE) { IPX_ADDRESS_BLOCK data[2]; LPSTR str[1] = {(LPSTR)Server->Name}; IpxAddrCpy (&data[0], &theNode->SN_Server); IpxAddrCpy (&data[1], Server); RouterLogWarningDataA (RouterEventLogHdl, ROUTERLOG_IPXSAP_SERVER_ADDRESS_CHANGE, 1, str, 24, (LPBYTE)data); } IpxAddrCpy (&theNode->SN_Server, Server); } // We already have server in the table if (IsDisabledNode (theNode)) // Just update the hop count theNode->SN_HopCount = Server->HopCount; else if (((Flags & SDB_DISABLED_NODE_FLAG) != (theNode->N_NodeFlags & SDB_DISABLED_NODE_FLAG)) || (Server->HopCount!=theNode->SN_HopCount)) { // Its hop count changed, we'll have to do something if (AcquireAllLocks ()) { theNode->SN_HopCount = Server->HopCount; if (mainNode==NULL) { // We haven't seen a node that had or has lower hop count // theNode is still the best if (Server->HopCount==IPX_MAX_HOP_COUNT) DeleteMainNode (theNode); else { if (IsEnumerator (CONTAINING_RECORD ( ServerTable.ST_ChangedSvrsQueue.PL_Head.Flink, SERVER_NODE, N_Links[SDB_CHANGE_QUEUE_LINK]))) ExpireLRRequests ((PVOID)UlongToPtr(InterfaceIndex)); // Move server to the bottom of change queue // so those who enumerate through it // notice that it has changed RemoveEntryList (&theNode->N_Links[SDB_CHANGE_QUEUE_LINK]); InsertHeadList (&ServerTable.ST_ChangedSvrsQueue.PL_Head, &theNode->N_Links[SDB_CHANGE_QUEUE_LINK]); } } else if (!IsMainNode (theNode) && (serverLink==&theNode->SN_ServerLink) && (Server->HopCountSN_ServerLink) // It is moving before the mainNode - becoming the best ChangeMainNode (mainNode, theNode, serverLink); else if (Server->HopCountSN_ServerLink); if (serverLink!=NULL) { // Going before the serverLink InsertTailList (serverLink, &theNode->SN_ServerLink); } else { // Going to the end of list (circular list: // end is right before the beginning InsertTailList (&mainNode->SN_ServerLink, &theNode->SN_ServerLink); } } else { // Going away (Server->HopCount>=IPX_MAX_HOP_COUNT) DeleteNode (theNode); } ReleaseAllLocks (); } else status = ERROR_GEN_FAILURE; } } else if (Server->HopCountType))!=NULL)) { ServerTable.ST_ServerCnt += 1; if (theNode->SN_Protocol==IPX_PROTOCOL_STATIC) ServerTable.ST_StaticServerCnt += 1; SetMainNode (theNode); // Insert in every list InsertTailList (cur, &theNode->N_Links[SDB_HASH_TABLE_LINK]); InsertTailList (link, &theNode->N_Links[SDB_TYPE_LIST_LINK]); InsertTailList (intfLink, &theNode->N_Links[SDB_INTF_LIST_LINK]); if (IsEnumerator (CONTAINING_RECORD ( ServerTable.ST_ChangedSvrsQueue.PL_Head.Flink, SERVER_NODE, N_Links[SDB_CHANGE_QUEUE_LINK]))) ExpireLRRequests ((PVOID)UlongToPtr(InterfaceIndex)); InsertHeadList (&ServerTable.ST_ChangedSvrsQueue.PL_Head, &theNode->N_Links[SDB_CHANGE_QUEUE_LINK]); link = FindSortedLink (Server->Type, Server->Name); InsertTailList (link, &theNode->N_Links[SDB_SORTED_LIST_LINK]); ServerTable.ST_TMPListCnt += 1; // Signal update if too many nodes if (ServerTable.ST_TMPListCnt == SDBMaxUnsortedServers) UpdateSortedList (); } else { HeapFree (ServerTable.ST_Heap, 0, theNode); status = ERROR_NOT_ENOUGH_MEMORY; } ReleaseAllLocks (); } else { HeapFree (ServerTable.ST_Heap, 0, theNode); status = ERROR_GEN_FAILURE; } } else status = ERROR_NOT_ENOUGH_MEMORY; } // Ok, we consider adding it although we have some entries already else { // Check for duplicates (different addresses) if ((IpxNetCmp (mainNode->SN_Server.Network, Server->Network)!=0) || (IpxNodeCmp (mainNode->SN_Server.Node, Server->Node)!=0) || (IpxSockCmp (mainNode->SN_Server.Socket, Server->Socket)!=0) ) { Trace (DEBUG_FAILURES, "Duplicate addresses for server %.4x %.48s:\n" " 1 - %.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x:%.2x%.2x" " from if-%ld, node-%.2x%.2x%.2x%.2x%.2x%.2x\n" " 2 - %.2x%.2x%.2x%.2x:%.2x%.2x%.2x%.2x%.2x%.2x:%.2x%.2x" " from if-%ld, node-%.2x%.2x%.2x%.2x%.2x%.2x", Server->Type, Server->Name, mainNode->SN_Server.Network[0], mainNode->SN_Server.Network[1], mainNode->SN_Server.Network[2], mainNode->SN_Server.Network[3], mainNode->SN_Server.Node[0], mainNode->SN_Server.Node[1], mainNode->SN_Server.Node[2], mainNode->SN_Server.Node[3], mainNode->SN_Server.Node[4], mainNode->SN_Server.Node[5], mainNode->SN_Server.Socket[0], mainNode->SN_Server.Socket[1], mainNode->SN_InterfaceIndex, mainNode->SN_AdvertisingNode[0], mainNode->SN_AdvertisingNode[1], mainNode->SN_AdvertisingNode[2], mainNode->SN_AdvertisingNode[3], mainNode->SN_AdvertisingNode[4], mainNode->SN_AdvertisingNode[5], Server->Network[0], Server->Network[1], Server->Network[2], Server->Network[3], Server->Node[0], Server->Node[1], Server->Node[2], Server->Node[3], Server->Node[4], Server->Node[5], Server->Socket[0], Server->Socket[1], InterfaceIndex, AdvertisingNode[0], AdvertisingNode[1], AdvertisingNode[2], AdvertisingNode[3], AdvertisingNode[4], AdvertisingNode[5] ); IF_LOG (EVENTLOG_WARNING_TYPE) { IPX_ADDRESS_BLOCK data[2]; LPSTR str[1] = {(LPSTR)Server->Name}; IpxAddrCpy (&data[0], &mainNode->SN_Server); IpxAddrCpy (&data[1], Server); RouterLogWarningDataA (RouterEventLogHdl, ROUTERLOG_IPXSAP_SERVER_DUPLICATE_ADDRESSES, 1, str, 24, (LPBYTE)data); } } // Collect all servers when routing if (Routing) { theNode = CreateNode (Server, InterfaceIndex, Protocol, AdvertisingNode, HashList); if (theNode!=NULL) { if (AcquireAllLocks ()) { if ((intfLink!=NULL) || ((intfLink=FindIntfLink (InterfaceIndex))!=NULL)) { if (theNode->SN_Protocol==IPX_PROTOCOL_STATIC) ServerTable.ST_StaticServerCnt += 1; InsertTailList (intfLink, &theNode->N_Links[SDB_INTF_LIST_LINK]); if ((Server->HopCountSN_HopCount) || IsDisabledNode (mainNode)) // Replaces the best node ChangeMainNode (mainNode, theNode, NULL); else if (serverLink!=NULL) { // Going before the serverLink InsertTailList (serverLink, &theNode->SN_ServerLink); } else { // Going to the end of list (circular list: // the end is right before the beginning) InsertTailList (&mainNode->SN_ServerLink, &theNode->SN_ServerLink); } } else { HeapFree (ServerTable.ST_Heap, 0, theNode); status = ERROR_GEN_FAILURE; } ReleaseAllLocks (); } else { HeapFree (ServerTable.ST_Heap, 0, theNode); status = ERROR_GEN_FAILURE; } } else status = ERROR_NOT_ENOUGH_MEMORY; } else if (serverLink!=NULL) { // If is better than one of ours if (AcquireAllLocks ()) { if ((intfLink!=NULL) || ((intfLink=FindIntfLink (InterfaceIndex))!=NULL)) { // Replace the worst one (at the end of server list) theNode = CONTAINING_RECORD ( mainNode->SN_ServerLink.Blink, SERVER_NODE, SN_ServerLink); VALIDATE_SERVER_NODE(theNode); IpxServerCpy (&theNode->SN_Server, Server); IpxNodeCpy (theNode->SN_AdvertisingNode, AdvertisingNode); theNode->SN_InterfaceIndex = InterfaceIndex; ResetDisabledNode (theNode); if (theNode->SN_Protocol!=Protocol) { if (Protocol==IPX_PROTOCOL_STATIC) ServerTable.ST_StaticServerCnt += 1; else if (theNode->SN_Protocol==IPX_PROTOCOL_STATIC) ServerTable.ST_StaticServerCnt -= 1; theNode->SN_Protocol = Protocol; } if (intfLink!=&theNode->N_Links[SDB_INTF_LIST_LINK]) { RemoveEntryList (&theNode->N_Links[SDB_INTF_LIST_LINK]); InsertTailList (intfLink, &theNode->N_Links[SDB_INTF_LIST_LINK]); } if (theNode==mainNode) { if (IsEnumerator (CONTAINING_RECORD ( ServerTable.ST_ChangedSvrsQueue.PL_Head.Flink, SERVER_NODE, N_Links[SDB_CHANGE_QUEUE_LINK]))) ExpireLRRequests ((PVOID)UlongToPtr(InterfaceIndex)); // It's already the best, just move it to the // bottom of change queue RemoveEntryList (&theNode->N_Links[SDB_CHANGE_QUEUE_LINK]); InsertHeadList (&ServerTable.ST_ChangedSvrsQueue.PL_Head, &theNode->N_Links[SDB_CHANGE_QUEUE_LINK]); } else if ((theNode->SN_HopCount < mainNode->SN_HopCount) || IsDisabledNode (mainNode)) // It replaces the best one ChangeMainNode (mainNode, theNode, serverLink); else if (serverLink!=&theNode->SN_ServerLink) { // It just gets in the middle RemoveEntryList (&theNode->SN_ServerLink); InsertTailList (serverLink, &theNode->SN_ServerLink); } } else status = ERROR_GEN_FAILURE; ReleaseAllLocks (); } else status = ERROR_GEN_FAILURE; } } } // Update position in expiration queue if ((status==NO_ERROR) && (theNode!=NULL) && (Server->HopCount!=IPX_MAX_HOP_COUNT) // theNode could not have ){ // been deleted or setup for deletion // Update flags theNode->N_NodeFlags = (theNode->N_NodeFlags & (~(SDB_DISABLED_NODE_FLAG|SDB_DONT_RESPOND_NODE_FLAG))) | (Flags & (SDB_DISABLED_NODE_FLAG|SDB_DONT_RESPOND_NODE_FLAG)); if (AcquireServerTableList (&ServerTable.ST_ExpirationQueue, TRUE)) { if (IsListEntry (&theNode->SN_TimerLink)) RemoveEntryList (&theNode->SN_TimerLink); if (TimeToLive!=INFINITE) { ASSERTMSG ("Invalid value of time to live ", TimeToLive*1000SN_ExpirationTime = GetTickCount()+TimeToLive*1000; RoundUpToSec (theNode->SN_ExpirationTime); // Scan expiration queue from the end (to minimize // the number of nodes we have to look through) cur = ServerTable.ST_ExpirationQueue.PL_Head.Blink; while (cur!=&ServerTable.ST_ExpirationQueue.PL_Head) { if (IsLater(theNode->SN_ExpirationTime, CONTAINING_RECORD ( cur, SERVER_NODE, SN_TimerLink)->SN_ExpirationTime)) break; cur = cur->Blink; } InsertHeadList (cur, &theNode->SN_TimerLink); if (cur==&ServerTable.ST_ExpirationQueue.PL_Head) { // Signal timer if server is in the beginning // of the list (we need to get a shot // earlier than we previously requested) LONGLONG timeout = (LONGLONG)TimeToLive*(1000*(-10000)); BOOL res = SetWaitableTimer ( ServerTable.ST_ExpirationTimer, (PLARGE_INTEGER)&timeout, 0, // no period NULL, NULL, // no completion FALSE); // no need to resume ASSERTERRMSG ("Could not set expiraton timer ", res); } } else { InitializeListEntry (&theNode->SN_TimerLink); } ReleaseServerTableList (&ServerTable.ST_ExpirationQueue); } else status = ERROR_GEN_FAILURE; } ReleaseServerTableList (&HashList->HL_List); return status; } /*++ ******************************************************************* C r e a t e N o d e Routine Description: Allocate and initialize new server entry Arguments: Server - server parameters (as it comes from IPX packet) InterfaceIndex - interface through which knowledge of server was obtained Protocol - protocol used to obtain server info AdvertisingNode - node from which this server info was received HashList - hash list to which this server belongs Return Value: Allocated and initialized entry NULL if allocation failed ******************************************************************* --*/ PSERVER_NODE CreateNode ( IN PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN DWORD Protocol, IN PUCHAR AdvertisingNode, IN PSDB_HASH_LIST HashList ) { PSERVER_NODE theNode; theNode = (PSERVER_NODE)HeapAlloc (ServerTable.ST_Heap, 0, sizeof (SERVER_NODE)); if (theNode==NULL) { Trace (DEBUG_FAILURES, "File: %s, line: %ld. Can't allocate server node (gle:%ld).", __FILE__, __LINE__, GetLastError ()); SetLastError (ERROR_NOT_ENOUGH_MEMORY); return NULL; } theNode->N_NodeFlags = SDB_SERVER_NODE; theNode->SN_HashList = HashList; theNode->SN_InterfaceIndex = InterfaceIndex; theNode->SN_Protocol = Protocol; theNode->SN_ObjectID = SDB_INVALID_OBJECT_ID; IpxNodeCpy (theNode->SN_AdvertisingNode, AdvertisingNode); theNode->SN_Signature = SDB_SERVER_NODE_SIGNATURE; IpxServerCpy (&theNode->SN_Server, Server); InitializeListEntry (&theNode->N_Links[SDB_HASH_TABLE_LINK]); InitializeListEntry (&theNode->N_Links[SDB_CHANGE_QUEUE_LINK]); InitializeListEntry (&theNode->N_Links[SDB_INTF_LIST_LINK]); InitializeListEntry (&theNode->N_Links[SDB_TYPE_LIST_LINK]); InitializeListEntry (&theNode->N_Links[SDB_SORTED_LIST_LINK]); InitializeListEntry (&theNode->SN_ServerLink); InitializeListEntry (&theNode->SN_TimerLink); return theNode; } /*++ ******************************************************************* C h a n g e M a i n N o d e Routine Description: Replace best entry for server (moves new best entry to the top of the server list, replaces old entry in hash, type, and sorted lists. Adds new entry to interface list if it is not already there All lists used for enumeration should be locked when calling this routine Arguments: oldNode - Current best entry newNode - Node that will become the best serverLink - Where oldNode has to go in server list: if newNode not in the list or serverLink==&oldNode->SN_ServerLink, oldNode gets pushed down by newNode if serverLink==NULL, oldNode goes to the end of list otherwise it goes before serverLink Return Value: NO_ERROR - server was added/updated ok other - reason of failure (windows error code) ******************************************************************* --*/ VOID ChangeMainNode ( IN PSERVER_NODE oldNode, IN PSERVER_NODE newNode, IN PLIST_ENTRY serverLink ) { ASSERTMSG ("Node is already main ", !IsMainNode (newNode)); SetMainNode (newNode); ASSERTMSG ("Node being reset is not main ", IsMainNode (oldNode)); ResetMainNode (oldNode); if (oldNode->SN_ObjectID!=SDB_INVALID_OBJECT_ID) { newNode->SN_ObjectID = oldNode->SN_ObjectID; oldNode->SN_ObjectID = SDB_INVALID_OBJECT_ID; } InsertTailList (&oldNode->N_Links[SDB_HASH_TABLE_LINK], &newNode->N_Links[SDB_HASH_TABLE_LINK]); RemoveEntryList (&oldNode->N_Links[SDB_HASH_TABLE_LINK]); InitializeListEntry (&oldNode->N_Links[SDB_HASH_TABLE_LINK]); RemoveEntryList (&oldNode->N_Links[SDB_CHANGE_QUEUE_LINK]); InitializeListEntry (&oldNode->N_Links[SDB_CHANGE_QUEUE_LINK]); if (IsEnumerator (CONTAINING_RECORD ( ServerTable.ST_ChangedSvrsQueue.PL_Head.Flink, SERVER_NODE, N_Links[SDB_CHANGE_QUEUE_LINK]))) ExpireLRRequests ((PVOID)UlongToPtr(newNode->SN_InterfaceIndex)); InsertHeadList (&ServerTable.ST_ChangedSvrsQueue.PL_Head, &newNode->N_Links[SDB_CHANGE_QUEUE_LINK]); InsertTailList (&oldNode->N_Links[SDB_TYPE_LIST_LINK], &newNode->N_Links[SDB_TYPE_LIST_LINK]); RemoveEntryList (&oldNode->N_Links[SDB_TYPE_LIST_LINK]); InitializeListEntry (&oldNode->N_Links[SDB_TYPE_LIST_LINK]); if (!IsListEntry (&newNode->SN_ServerLink)) { InsertTailList (&oldNode->SN_ServerLink, &newNode->SN_ServerLink); } else if (serverLink==&oldNode->SN_ServerLink) { RemoveEntryList (&newNode->SN_ServerLink); InsertTailList (&oldNode->SN_ServerLink, &newNode->SN_ServerLink); } else if (serverLink!=NULL) { RemoveEntryList (&oldNode->SN_ServerLink); InsertHeadList (serverLink, &oldNode->SN_ServerLink); } if (oldNode->SN_HopCount==IPX_MAX_HOP_COUNT) { DeleteNode (oldNode); } serverLink = FindSortedLink (newNode->SN_Server.Type, newNode->SN_Server.Name); if (!IsListEntry (&newNode->N_Links[SDB_SORTED_LIST_LINK])) { InsertTailList (serverLink, &newNode->N_Links[SDB_SORTED_LIST_LINK]); ServerTable.ST_TMPListCnt += 1; if (ServerTable.ST_TMPListCnt == SDBMaxUnsortedServers) UpdateSortedList (); } } /*++ ******************************************************************* A c q u i r e A l l L o c k s Routine Description: Acquire locks for all lists that are updated immediately when server is added/deleted/updated Arguments: None Return Value: NO_ERROR - server was added/updated ok other - reason of failure (windows error code) ******************************************************************* --*/ BOOL AcquireAllLocks ( void ) { if (AcquireServerTableList (&ServerTable.ST_ChangedSvrsQueue, TRUE)) { if (AcquireServerTableList (&ServerTable.ST_IntfList, TRUE)) { if (AcquireServerTableList (&ServerTable.ST_TypeList, TRUE)) { if (AcquireServerTableList (&ServerTable.ST_SortedListTMP, TRUE)) return TRUE; ReleaseServerTableList (&ServerTable.ST_TypeList); } ReleaseServerTableList (&ServerTable.ST_IntfList); } ReleaseServerTableList (&ServerTable.ST_ChangedSvrsQueue); } return FALSE; } /*++ ******************************************************************* R e l e a s e A l l L o c k s Routine Description: Release locks for all lists that are updated immediately when server is added/deleted/updated Arguments: None Return value: None ******************************************************************* --*/ VOID ReleaseAllLocks ( void ) { ReleaseServerTableList (&ServerTable.ST_SortedListTMP); ReleaseServerTableList (&ServerTable.ST_TypeList); ReleaseServerTableList (&ServerTable.ST_IntfList); ReleaseServerTableList (&ServerTable.ST_ChangedSvrsQueue); } /*++ ******************************************************************* D e l e t e M a i n N o d e Routine Description: Delete entry that was best (it still remains in the table for a while until all interested get a chance to learn this All lists used for enumeration should be locked when calling this routine Arguments: node - entry to delete Return Value: None ******************************************************************* --*/ VOID DeleteMainNode ( IN PSERVER_NODE node ) { RemoveEntryList (&node->N_Links[SDB_HASH_TABLE_LINK]); InitializeListEntry (&node->N_Links[SDB_HASH_TABLE_LINK]); RemoveEntryList (&node->N_Links[SDB_CHANGE_QUEUE_LINK]); InitializeListEntry (&node->N_Links[SDB_CHANGE_QUEUE_LINK]); RemoveEntryList (&node->N_Links[SDB_INTF_LIST_LINK]); InitializeListEntry (&node->N_Links[SDB_INTF_LIST_LINK]); RemoveEntryList (&node->N_Links[SDB_TYPE_LIST_LINK]); InitializeListEntry (&node->N_Links[SDB_TYPE_LIST_LINK]); ServerTable.ST_ServerCnt -= 1; if (node->SN_Protocol==IPX_PROTOCOL_STATIC) ServerTable.ST_StaticServerCnt -= 1; if (ServerTable.ST_LastEnumerator==NULL) { ASSERTMSG ("Node being reset is not main ", IsMainNode (node)); ResetMainNode (node); // We won't try to get access to sorted list because it is // slow, the entry will be actually removed from it // and disposed of when the sorted list is updated if (AcquireServerTableList (&ServerTable.ST_DeletedList, TRUE)) { InsertTailList (&ServerTable.ST_DeletedList.PL_Head, &node->SN_ServerLink); ServerTable.ST_DeletedListCnt += 1; if (ServerTable.ST_DeletedListCnt==SDBMaxUnsortedServers) UpdateSortedList (); ReleaseServerTableList (&ServerTable.ST_DeletedList); } // If we fail in locking we just let it hang around // (at least we won't risk damaging the list) } else { // If there are enumerators in change queue, we can't // delete the node until they see it if (IsEnumerator (CONTAINING_RECORD ( ServerTable.ST_ChangedSvrsQueue.PL_Head.Flink, SERVER_NODE, N_Links[SDB_CHANGE_QUEUE_LINK]))) ExpireLRRequests ((PVOID)UlongToPtr(node->SN_InterfaceIndex)); InsertHeadList (&ServerTable.ST_ChangedSvrsQueue.PL_Head, &node->N_Links[SDB_CHANGE_QUEUE_LINK]); } } /*++ ******************************************************************* D e l e t e N o d e Routine Description: Delete entry that was not the best All lists used for enumeration should be locked when calling this routine Arguments: node - entry to delete Return Value: None ******************************************************************* --*/ VOID DeleteNode ( IN PSERVER_NODE node ) { RemoveEntryList (&node->N_Links[SDB_INTF_LIST_LINK]); InitializeListEntry (&node->N_Links[SDB_INTF_LIST_LINK]); RemoveEntryList (&node->SN_ServerLink); if (node->SN_Protocol==IPX_PROTOCOL_STATIC) ServerTable.ST_StaticServerCnt -= 1; if (AcquireServerTableList (&ServerTable.ST_DeletedList, TRUE)) { // We won't try to get access to sorted list because it is // slow, the entry will be actually removed from it // and disposed of when the sorted list is updated InsertTailList (&ServerTable.ST_DeletedList.PL_Head, &node->SN_ServerLink); ServerTable.ST_DeletedListCnt += 1; if (ServerTable.ST_DeletedListCnt==SDBMaxUnsortedServers) UpdateSortedList (); ReleaseServerTableList (&ServerTable.ST_DeletedList); } else { // If we fail in locking we just let it hang around // (at least we won't risk damaging the list) InitializeListEntry (&node->SN_ServerLink); } } /*++ ******************************************************************* D o U p d a t e S o r t e d L i s t Routine Description: Deletes entries placed in deleted list and merges temporary and permanent sorted lists. This routine may take some time to execute because it may need to scan the whole sorted list that contains all entries in the table Arguments: None Return Value: None ******************************************************************* --*/ VOID DoUpdateSortedList ( void ) { PLIST_ENTRY cur; ULONG curCount; LIST_ENTRY tempHead; // We first lock the 'slow' list if (!AcquireServerTableList (&ServerTable.ST_SortedListPRM, TRUE)) // Failure to acquire sorted list, // tell them to retry in a little while return ; // The following two list are locked for a short period: // we'll just delete what needs to be deleted (no searching) // and copy and reset temp sorted list if (!AcquireServerTableList (&ServerTable.ST_ExpirationQueue, TRUE)) { ReleaseServerTableList (&ServerTable.ST_SortedListPRM); // Failure to acquire expiration queue, // tell them to retry in a little while return ; } if (!AcquireServerTableList (&ServerTable.ST_SortedListTMP, TRUE)) { ReleaseServerTableList (&ServerTable.ST_ExpirationQueue); ReleaseServerTableList (&ServerTable.ST_SortedListPRM); // Failure to acquire sorted list, // tell them to retry in a little while return ; } if (!AcquireServerTableList (&ServerTable.ST_DeletedList, TRUE)) { ReleaseServerTableList (&ServerTable.ST_SortedListTMP); ReleaseServerTableList (&ServerTable.ST_ExpirationQueue); ReleaseServerTableList (&ServerTable.ST_SortedListPRM); // Failure to acquire deleted list, // tell them to retry in a little while return ; } // Delete what we have to delete cur = ServerTable.ST_DeletedList.PL_Head.Flink; while (cur != &ServerTable.ST_DeletedList.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, SN_ServerLink); VALIDATE_SERVER_NODE(node); cur = cur->Flink; RemoveEntryList (&node->SN_ServerLink); if (IsListEntry (&node->N_Links[SDB_SORTED_LIST_LINK])) { RemoveEntryList (&node->N_Links[SDB_SORTED_LIST_LINK]); } if (IsListEntry (&node->SN_TimerLink)) { RemoveEntryList (&node->SN_TimerLink); } ASSERTMSG ("Deleted entry is still in hash list ", !IsListEntry (&node->N_Links[SDB_HASH_TABLE_LINK])); ASSERTMSG ("Deleted entry is still in change queue ", !IsListEntry (&node->N_Links[SDB_CHANGE_QUEUE_LINK])); ASSERTMSG ("Deleted entry is still in interface list ", !IsListEntry (&node->N_Links[SDB_INTF_LIST_LINK])); ASSERTMSG ("Deleted entry is still in type list ", !IsListEntry (&node->N_Links[SDB_TYPE_LIST_LINK])); HeapFree (ServerTable.ST_Heap, 0, node); } ReleaseServerTableList (&ServerTable.ST_ExpirationQueue); ServerTable.ST_DeletedListCnt = 0; ReleaseServerTableList (&ServerTable.ST_DeletedList); // Now, just copy the head of the temp list, // so we won't delay others while processing it if (!IsListEmpty (&ServerTable.ST_SortedListTMP.PL_Head)) { InsertTailList (&ServerTable.ST_SortedListTMP.PL_Head, &tempHead); RemoveEntryList (&ServerTable.ST_SortedListTMP.PL_Head); InitializeListHead (&ServerTable.ST_SortedListTMP.PL_Head); } else InitializeListHead (&tempHead); ServerTable.ST_TMPListCnt = 0; // We are going to remove all of them, ReleaseServerTableList (&ServerTable.ST_SortedListTMP); // Now we start the merge cur = ServerTable.ST_SortedListPRM.PL_Head.Flink; while (!IsListEmpty (&tempHead)) { PSERVER_NODE prmNode = NULL; PSERVER_NODE tmpNode; tmpNode = CONTAINING_RECORD (tempHead.Flink, SERVER_NODE, N_Links[SDB_SORTED_LIST_LINK]); VALIDATE_SERVER_NODE(tmpNode); while (cur!=&ServerTable.ST_SortedListPRM.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[SDB_SORTED_LIST_LINK]); VALIDATE_NODE(node); if (!IsEnumerator (node)) { if (tmpNode->SN_Server.Type==node->SN_Server.Type) { INT res = IpxNameCmp (tmpNode->SN_Server.Name, node->SN_Server.Name); if (res==0) { cur = cur->Flink; prmNode = node; break; } else if (res<0) break; } else if (tmpNode->SN_Server.TypeSN_Server.Type) break; } cur = cur->Flink; } if (AcquireServerTableList (&tmpNode->SN_HashList->HL_List, TRUE)) { if (AcquireServerTableList (&ServerTable.ST_SortedListTMP, TRUE)) { RemoveEntryList (&tmpNode->N_Links[SDB_SORTED_LIST_LINK]); if (IsMainNode (tmpNode)) { ASSERTMSG ("Node marked as sorted in temp list ", !IsSortedNode (tmpNode)); SetSortedNode (tmpNode); InsertTailList (cur, &tmpNode->N_Links[SDB_SORTED_LIST_LINK]); if (prmNode!=NULL) { ASSERTMSG ("Node not marked as sorted in sorted list ", IsSortedNode (prmNode)); RemoveEntryList (&prmNode->N_Links[SDB_SORTED_LIST_LINK]); InitializeListEntry (&prmNode->N_Links[SDB_SORTED_LIST_LINK]); ResetSortedNode (prmNode); } } else { InitializeListEntry (&tmpNode->N_Links[SDB_SORTED_LIST_LINK]); } ReleaseServerTableList (&ServerTable.ST_SortedListTMP); } else Sleep (SAP_ERROR_COOL_OFF_TIME); ReleaseServerTableList (&tmpNode->SN_HashList->HL_List); } else Sleep (SAP_ERROR_COOL_OFF_TIME); } ReleaseServerTableList (&ServerTable.ST_SortedListPRM); } VOID APIENTRY UpdateSortedListWorker ( PVOID context ) { do { InterlockedExchange (&ServerTable.ST_UpdatePending, 0); DoUpdateSortedList (); } while (InterlockedDecrement (&ServerTable.ST_UpdatePending)>=0); } /*++ ******************************************************************* U p d a t e S o r t e d L i s t Routine Description: Schedules work item to update sorted list. Should be called whenever UpdateObject is signalled Arguments: None Return Value: None ******************************************************************* --*/ VOID UpdateSortedList ( void ) { BOOL res; LONGLONG timeout=(LONGLONG)SDBSortLatency*(-10000); static WORKERFUNCTION worker=&UpdateSortedListWorker; res = SetWaitableTimer (ServerTable.ST_UpdateTimer, (PLARGE_INTEGER)&timeout, 0, // no period NULL, NULL, // no completion FALSE); // no need to resume ASSERTMSG ("Could not set update timer ", res); if (InterlockedIncrement (&ServerTable.ST_UpdatePending)==0) ScheduleWorkItem (&worker); } /*++ ******************************************************************* P r o c e s s E x p i r a t i o n Q u e u e Routine Description: Deletes expired servers from the table and set timer object to be signalled when next item in expiration queue is due Arguments: None Return Value: None ******************************************************************* --*/ VOID ProcessExpirationQueue ( void ) { ULONG curTime = GetTickCount (); ULONG dueTime = curTime + MAXULONG/2; LONGLONG timeout; BOOL res; if (!AcquireServerTableList (&ServerTable.ST_ExpirationQueue, TRUE)) return ; while (!IsListEmpty (&ServerTable.ST_ExpirationQueue.PL_Head)) { PSDB_HASH_LIST HashList; PSERVER_NODE node = CONTAINING_RECORD ( ServerTable.ST_ExpirationQueue.PL_Head.Flink, SERVER_NODE, SN_TimerLink); VALIDATE_SERVER_NODE(node); if (IsLater(node->SN_ExpirationTime,curTime)) { dueTime = node->SN_ExpirationTime; break; } HashList = node->SN_HashList; // Try to get access to hash list but do not wait because // we may create a deadlock if (!AcquireServerTableList (&HashList->HL_List, FALSE)) { // Hash list is locked, we'll have to release the timer queue // and reacquire it again after securing the hash list ReleaseServerTableList (&ServerTable.ST_ExpirationQueue); if (AcquireServerTableList (&HashList->HL_List, TRUE)) { if (AcquireServerTableList (&ServerTable.ST_ExpirationQueue, TRUE)) { // Make sure entry is still there if (ServerTable.ST_ExpirationQueue.PL_Head.Flink !=&node->SN_TimerLink) { // Gone already, go to the next one ReleaseServerTableList (&HashList->HL_List); continue; } } else { // Failure to regain expiration queue, // tell them to retry in a little while ReleaseServerTableList (&HashList->HL_List); return ; } } else // Failure to acquire hash list, // tell them to retry in a little while return ; } // At this point we have hash list and expiration queue locks // we can proceed with deletion RemoveEntryList (&node->SN_TimerLink); InitializeListEntry (&node->SN_TimerLink); if (node->SN_HopCount!=IPX_MAX_HOP_COUNT) { // It might have been already prepeared for deletion if (AcquireAllLocks ()) { // Need to have all locks before changing // node info node->SN_HopCount = IPX_MAX_HOP_COUNT; if (IsMainNode (node)) { if (IsListEmpty (&node->SN_ServerLink)) DeleteMainNode (node); else ChangeMainNode ( node, CONTAINING_RECORD ( node->SN_ServerLink.Flink, SERVER_NODE, SN_ServerLink), NULL); } else DeleteNode (node); ReleaseAllLocks (); } } ReleaseServerTableList (&HashList->HL_List); } ReleaseServerTableList (&ServerTable.ST_ExpirationQueue); timeout = (LONGLONG)(dueTime-curTime)*(-10000); res = SetWaitableTimer (ServerTable.ST_ExpirationTimer, (PLARGE_INTEGER)&timeout, 0, // no period NULL, NULL, // no completion FALSE); // no need to resume ASSERTMSG ("Could not set expiration timer ", res); } /*++ ******************************************************************* Q u e r y S e r v e r Routine Description: Checks if server with given type and name exists in the table Returns TRUE if it does and fills out requested server info with data of the best entry for the server Arguments: Type - server type Name - server name Server - buffer in which to put server info InterfaceIndex - buffer in which to put server interface index Protocol - buffer in which to put server protocol ObjectID - buffer in which to put server object id (number that uniquely identifies server (the whole set of entries, not just the best one) in the table; it is valid for very long but FINITE period of time) Return Value: TRUE - server was found FALSE - server was not found or operation failed (call GetLastError() to find out the reason for failure if any) ******************************************************************* --*/ BOOL QueryServer ( IN USHORT Type, IN PUCHAR Name, OUT PIPX_SERVER_ENTRY_P Server OPTIONAL, OUT PULONG InterfaceIndex OPTIONAL, OUT PULONG Protocol OPTIONAL, OUT PULONG ObjectID OPTIONAL ) { PSDB_HASH_LIST HashList; PLIST_ENTRY cur; PSERVER_NODE theNode = NULL; INT res; if (Name[0]==0) { Trace (DEBUG_SERVERDB, "Illigal server name in QueryServer."); SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } HashList = &ServerTable.ST_HashLists[HashFunction (Name)]; if (!AcquireServerTableList (&HashList->HL_List, TRUE)) { SetLastError (ERROR_GEN_FAILURE); return FALSE; } cur = HashList->HL_List.PL_Head.Flink; while (cur!=&HashList->HL_List.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[SDB_HASH_TABLE_LINK]); VALIDATE_NODE(node); if (!IsEnumerator (node) && !IsDisabledNode (node) && (node->SN_Server.HopCount < IPX_MAX_HOP_COUNT)) { if (Type == node->SN_Server.Type) { res = IpxNameCmp (Name, node->SN_Server.Name); if (res==0) { theNode = node; break; } else if (res<0) break; } else if (TypeSN_Server.Type) break; } cur = cur->Flink; } if (theNode!=NULL) { if (ARGUMENT_PRESENT (Server)) IpxServerCpy (Server, &theNode->SN_Server); if (ARGUMENT_PRESENT (InterfaceIndex)) *InterfaceIndex = theNode->SN_InterfaceIndex; if (ARGUMENT_PRESENT (Protocol)) *Protocol = theNode->SN_Protocol; if (ARGUMENT_PRESENT (ObjectID)) { if (theNode->SN_ObjectID==SDB_INVALID_OBJECT_ID) theNode->SN_ObjectID = GenerateUniqueID (theNode->SN_HashList); *ObjectID = theNode->SN_ObjectID; } res = TRUE; } else { SetLastError (NO_ERROR); res = FALSE; } ReleaseServerTableList (&HashList->HL_List); return res==TRUE; } /*++ ******************************************************************* G e t S e r v e r F r o m I D Routine Description: Returns info for server with specified ID Arguments: ObjectID - server object id (number that uniquely identifies server in the table, it is valid for very long but FINITE amount of time) Server - buffer in which to put server info InterfaceIndex - buffer in which to put server interface index Protocol - buffer in which to put server protocol Return Value: TRUE - server was found FALSE - server was not found or operation failed (call GetLastError() to find out the reason for failure if any) ******************************************************************* --*/ BOOL GetServerFromID ( IN ULONG ObjectID, OUT PIPX_SERVER_ENTRY_P Server OPTIONAL, OUT PULONG InterfaceIndex OPTIONAL, OUT PULONG Protocol OPTIONAL ) { PSDB_HASH_LIST HashList; PLIST_ENTRY cur; PSERVER_NODE theNode = NULL; INT res; HashList = &ServerTable.ST_HashLists[ObjectID%SDB_NAME_HASH_SIZE]; if (!AcquireServerTableList (&HashList->HL_List, TRUE)) { SetLastError (ERROR_GEN_FAILURE); return FALSE; } cur = HashList->HL_List.PL_Head.Flink; while (cur!=&HashList->HL_List.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[SDB_HASH_TABLE_LINK]); VALIDATE_NODE(node); if (!IsEnumerator (node) && !IsDisabledNode (node) && (node->SN_HopCount < IPX_MAX_HOP_COUNT) && (node->SN_ObjectID == ObjectID)) { theNode = node; break; } cur = cur->Flink; } if (theNode!=NULL) { if (ARGUMENT_PRESENT (Server)) IpxServerCpy (Server, &theNode->SN_Server); if (ARGUMENT_PRESENT (InterfaceIndex)) *InterfaceIndex = theNode->SN_InterfaceIndex; if (ARGUMENT_PRESENT (Protocol)) *Protocol = theNode->SN_Protocol; res = TRUE; } else { SetLastError (NO_ERROR); res = FALSE; } ReleaseServerTableList (&HashList->HL_List); return res==TRUE; } /*++ ******************************************************************* C r e a t e L i s t E n u m e r a t o r Routine Description: Creates enumerator node that allows scanning through the server table lists Arguments: ListIdx - index of list through which to scan (currently supported lists are: hash lists, interface lists, type lists, changed servers queue Type - limits enumeration to servers of specific type and indentifies a particular type list if index is SDB_TYPE_LIST_IDX (use 0xFFFF to return all server and/or to go through all type lists) Name - limits enumeration to servers with certain name if present InterfaceIndex - limits enumeration to servers of specific interface and indentifies a particular interface list if index is SDB_INTF_LIST_IDX (use INVALID_INTERFACE_INDEX to return all server and/or to go through all interface lists) Protocol - limits enumeration to servers of certain protocol (0xFFFFFFFF - all protocols) Flags - identifies additional conditions on entries enumerated: SDB_MAIN_NODE_FLAG - only best servers SDB_DISABLED_NODE_FLAG - include disabled servers Return Value: Handle that represents the enumeration node NULL if specified list does not exist or operation failed (call GetLastError () for the reason of failure if any) ******************************************************************* --*/ HANDLE CreateListEnumerator ( IN INT ListIdx, IN USHORT Type, IN PUCHAR Name OPTIONAL, IN ULONG InterfaceIndex, IN ULONG Protocol, IN INT Flags ) { HANDLE hEnum; #define enumNode ((PENUMERATOR_NODE)hEnum) hEnum = (HANDLE)GlobalAlloc (GPTR, sizeof (ENUMERATOR_NODE)); if (hEnum==NULL) { Trace (DEBUG_FAILURES, "File: %s, line: %ld. Can't allocate enumerator node (gle:%ld).", __FILE__, __LINE__, GetLastError ()); SetLastError (ERROR_NOT_ENOUGH_MEMORY); return NULL; } InitializeListEntry (&enumNode->N_Links[ListIdx]); enumNode->N_NodeFlags = SDB_ENUMERATION_NODE; enumNode->EN_LinkIdx = ListIdx; enumNode->EN_InterfaceIndex = InterfaceIndex; enumNode->EN_Protocol = Protocol; enumNode->EN_Signature = SDB_ENUMERATOR_NODE_SIGNATURE; enumNode->EN_Type = Type; if (ARGUMENT_PRESENT(Name)) { if (Name[0]!=0) { enumNode->EN_ListLock = &ServerTable.ST_HashLists[HashFunction(Name)].HL_List; IpxNameCpy (enumNode->EN_Name, Name); } else { Trace (DEBUG_SERVERDB, "Illigal server name in CreateListEnumerator."); GlobalFree (enumNode); SetLastError (ERROR_INVALID_PARAMETER); return NULL; } } else enumNode->EN_Name[0] = 0; enumNode->EN_Flags = Flags; switch (ListIdx) { case SDB_HASH_TABLE_LINK: if (enumNode->EN_Name[0]==0) enumNode->EN_ListLock = &ServerTable.ST_HashLists[0].HL_List; break; case SDB_CHANGE_QUEUE_LINK: enumNode->EN_ListLock = &ServerTable.ST_ChangedSvrsQueue; break; case SDB_INTF_LIST_LINK: enumNode->EN_ListLock = &ServerTable.ST_IntfList; break; case SDB_TYPE_LIST_LINK: enumNode->EN_ListLock = &ServerTable.ST_TypeList; break; default: ASSERTMSG ("Invalid list index. ", FALSE); GlobalFree (hEnum); SetLastError (ERROR_INVALID_PARAMETER); return NULL; } if (!AcquireServerTableList (enumNode->EN_ListLock, TRUE)) { GlobalFree (hEnum); SetLastError (ERROR_GEN_FAILURE); return NULL; } // All enumeration go in the direction opposite to // direction of insertion to exclude the possibility of // returning the same server twice (this may happen if // server entry gets deleted and another one is inserted in // the same place while client processes the result of // enumeration callback switch (ListIdx) { case SDB_HASH_TABLE_LINK: enumNode->EN_ListHead = &enumNode->EN_ListLock->PL_Head; // Insert in the tail of the list -> we go backwards InsertTailList (enumNode->EN_ListHead, &enumNode->N_Links[enumNode->EN_LinkIdx]); break; case SDB_CHANGE_QUEUE_LINK: enumNode->EN_ListHead = &ServerTable.ST_ChangedSvrsQueue.PL_Head; // Insert in the head, because we want client to see only // the newly changed servers that will be inserted in the // bottom (head) of the list InsertHeadList (enumNode->EN_ListHead, &enumNode->N_Links[enumNode->EN_LinkIdx]); // Increment number of enumerating clients (we remove deleted // server entries from the change queue once all enumerating clients // get a chance to see it) if (ServerTable.ST_LastEnumerator==NULL) ServerTable.ST_LastEnumerator = hEnum; break; case SDB_INTF_LIST_LINK: if (enumNode->EN_InterfaceIndex==INVALID_INTERFACE_INDEX) { if (!IsListEmpty (&ServerTable.ST_IntfList.PL_Head)) { PINTF_NODE intfNode = CONTAINING_RECORD ( ServerTable.ST_IntfList.PL_Head.Flink, INTF_NODE, IN_Link); enumNode->EN_ListHead = &intfNode->IN_Head; // Insert in the tail of the list -> we go backwards InsertTailList (enumNode->EN_ListHead, &enumNode->N_Links[enumNode->EN_LinkIdx]); break; } // No interface lists - fall through to error handling } else { enumNode->EN_ListHead = FindIntfLink (InterfaceIndex); if (enumNode->EN_ListHead!=NULL) { // Insert in the tail of the list -> we go backwards InsertTailList (enumNode->EN_ListHead, &enumNode->N_Links[enumNode->EN_LinkIdx]); break; } // Interface list could not be found - // fall through to error handling } GlobalFree (hEnum); SetLastError (NO_ERROR); hEnum = NULL; break; case SDB_TYPE_LIST_LINK: if (enumNode->EN_Type==0xFFFF) { if (!IsListEmpty (&ServerTable.ST_TypeList.PL_Head)) { PTYPE_NODE typeNode = CONTAINING_RECORD ( ServerTable.ST_TypeList.PL_Head.Flink, TYPE_NODE, TN_Link); enumNode->EN_ListHead = &typeNode->TN_Head; // Insert in the tail of the list -> we go backwards InsertTailList (enumNode->EN_ListHead, &enumNode->N_Links[enumNode->EN_LinkIdx]); break; } // No type lists - fall through to error handling } else { enumNode->EN_ListHead = FindTypeLink (Type); if (enumNode->EN_ListHead!=NULL) { // Insert in the tail of the list -> we go backwards InsertTailList (enumNode->EN_ListHead, &enumNode->N_Links[enumNode->EN_LinkIdx]); break; } // Type list could not be found - // fall through to error handling } GlobalFree (hEnum); SetLastError (NO_ERROR); hEnum = NULL; } if (enumNode) { ReleaseServerTableList (enumNode->EN_ListLock); } #undef enumNode return hEnum; } /*++ ******************************************************************* E n u m e r a t e S e r v e r s Routine Description: Calls callback routine consequtively for servers in the enumerated list until told to stop by the callback or end of list is reached Arguments: Enumerator - handle obtained from CreateListEnumerator CallBackProc - function to call for each server in the list CBParam - extra parameter to pass to callback function Return Value: TRUE - if stopped by the callback FALSE - if end of list is reached or operation failed (call GetLastError () to find out the reason of failure) ******************************************************************* --*/ BOOLEAN EnumerateServers ( IN HANDLE Enumerator, IN EnumerateServersCallBack CallBackProc, IN LPVOID CBParam ) { #define enumNode ((PENUMERATOR_NODE)Enumerator) BOOL res=FALSE, bNeedHashLock; PSERVER_NODE node; ULONG releaseTime; // The following callbacks need to be invoked with hash table // lock held because they modify/delete nodes bNeedHashLock = (enumNode->EN_LinkIdx!=SDB_HASH_TABLE_LINK) && ((CallBackProc==DeleteAllServersCB) || (CallBackProc==DeleteNonLocalServersCB) || (CallBackProc==EnableAllServersCB) || (CallBackProc==DisableAllServersCB) || (CallBackProc==ConvertToStaticCB) || (CallBackProc==DeleteAllServersCB)); VALIDATE_ENUMERATOR_NODE(Enumerator); if (!AcquireServerTableList (enumNode->EN_ListLock, TRUE)) { SetLastError (ERROR_GEN_FAILURE); return FALSE; } releaseTime = GetTickCount ()+SDB_MAX_LOCK_HOLDING_TIME; do { // Loop till told to stop by the callback // Don't let client hold the list for too long if (IsLater (GetTickCount (),releaseTime)) { ReleaseServerTableList (enumNode->EN_ListLock); #if DBG Trace (DEBUG_SERVERDB, "Held enumeration lock (%d list) for %ld extra msec", enumNode->EN_LinkIdx, GetTickCount ()-releaseTime); #endif AcquireServerTableList (enumNode->EN_ListLock, TRUE); releaseTime = GetTickCount ()+SDB_MAX_LOCK_HOLDING_TIME; } // Check if end of the list is reached while (enumNode->N_Links[enumNode->EN_LinkIdx].Blink ==enumNode->EN_ListHead) { // Check if we asked and can go to another list switch (enumNode->EN_LinkIdx) { case SDB_HASH_TABLE_LINK: if ((enumNode->EN_Name[0]==0) &&(enumNode->EN_ListHead <&ServerTable.ST_HashLists[SDB_NAME_HASH_SIZE-1].HL_List.PL_Head)) { RemoveEntryList ( &enumNode->N_Links[enumNode->EN_LinkIdx]); ReleaseServerTableList (enumNode->EN_ListLock); enumNode->EN_ListLock = &(CONTAINING_RECORD ( enumNode->EN_ListLock, SDB_HASH_LIST, HL_List)+1)->HL_List; enumNode->EN_ListHead = &enumNode->EN_ListLock->PL_Head; if (!AcquireServerTableList (enumNode->EN_ListLock, TRUE)) { InitializeListEntry ( &enumNode->N_Links[enumNode->EN_LinkIdx]); SetLastError (ERROR_GEN_FAILURE); return FALSE; } releaseTime = GetTickCount () +SDB_MAX_LOCK_HOLDING_TIME; InsertTailList (enumNode->EN_ListHead, &enumNode->N_Links[enumNode->EN_LinkIdx]); continue; } break; case SDB_INTF_LIST_LINK: if (enumNode->EN_InterfaceIndex ==INVALID_INTERFACE_INDEX) { PINTF_NODE intfNode = CONTAINING_RECORD ( enumNode->EN_ListHead, INTF_NODE, IN_Head); if (intfNode->IN_Link.Flink !=&ServerTable.ST_IntfList.PL_Head) { enumNode->EN_ListHead = &(CONTAINING_RECORD ( intfNode->IN_Link.Flink, INTF_NODE, IN_Link)->IN_Head); RemoveEntryList ( &enumNode->N_Links[enumNode->EN_LinkIdx]); InsertTailList (enumNode->EN_ListHead, &enumNode->N_Links[enumNode->EN_LinkIdx]); continue; } } break; case SDB_TYPE_LIST_LINK: if (enumNode->EN_Type == 0xFFFF) { PTYPE_NODE typeNode = CONTAINING_RECORD ( enumNode->EN_ListHead, TYPE_NODE, TN_Head); if (typeNode->TN_Link.Flink !=&ServerTable.ST_TypeList.PL_Head) { enumNode->EN_ListHead = &(CONTAINING_RECORD ( typeNode->TN_Link.Flink, TYPE_NODE, TN_Link)->TN_Head); RemoveEntryList ( &enumNode->N_Links[enumNode->EN_LinkIdx]); InsertTailList (enumNode->EN_ListHead, &enumNode->N_Links[enumNode->EN_LinkIdx]); continue; } } break; case SDB_CHANGE_QUEUE_LINK: break; default: ASSERTMSG ("Unsupported list index ", FALSE); } // No more lists or not asked to check all of them ReleaseServerTableList (enumNode->EN_ListLock); SetLastError (NO_ERROR); return FALSE; } node = CONTAINING_RECORD (enumNode->N_Links[enumNode->EN_LinkIdx].Blink, SERVER_NODE, N_Links[enumNode->EN_LinkIdx]); VALIDATE_NODE(node); RemoveEntryList (&enumNode->N_Links[enumNode->EN_LinkIdx]); InsertTailList (&node->N_Links[enumNode->EN_LinkIdx], &enumNode->N_Links[enumNode->EN_LinkIdx]); if (!IsEnumerator(node) && ((enumNode->EN_Flags & SDB_DISABLED_NODE_FLAG) || !IsDisabledNode (node)) && (!(enumNode->EN_Flags & SDB_MAIN_NODE_FLAG) || IsMainNode (node)) && ((enumNode->EN_InterfaceIndex==INVALID_INTERFACE_INDEX) || (enumNode->EN_InterfaceIndex==node->SN_InterfaceIndex)) && ((enumNode->EN_Type==0xFFFF) || (enumNode->EN_Type==node->SN_Type)) && ((enumNode->EN_Protocol==0xFFFFFFFF) || (enumNode->EN_Protocol==node->SN_Protocol)) && ((enumNode->EN_Name[0]==0) || (IpxNameCmp(enumNode->EN_Name, node->SN_Name)!=0)) ) { PSDB_HASH_LIST HashList; if (bNeedHashLock) { HashList = node->SN_HashList; // Release the non-hash table lock to prevent deadlock ReleaseServerTableList (enumNode->EN_ListLock); if (!AcquireServerTableList (&HashList->HL_List, TRUE)) { SetLastError (ERROR_GEN_FAILURE); return FALSE; } // Make sure the node was not deleted when we were // acquiring hash lock if (enumNode->N_Links[enumNode->EN_LinkIdx].Flink !=&node->N_Links[enumNode->EN_LinkIdx]) { // Node is gone, continue with the next one ReleaseServerTableList (&HashList->HL_List); if (AcquireServerTableList (enumNode->EN_ListLock, TRUE)) continue; else { SetLastError (ERROR_GEN_FAILURE); return FALSE; } } } // Check if we need to go through server list if (!(enumNode->EN_Flags & SDB_MAIN_NODE_FLAG) && !IsListEmpty (&node->SN_ServerLink) && (enumNode->EN_LinkIdx!=SDB_INTF_LIST_LINK) // Interface lists contain all entries anyway ) { PLIST_ENTRY cur; BOOL bMainNode; cur = node->SN_ServerLink.Blink; do { PSERVER_NODE node1 = CONTAINING_RECORD (cur, SERVER_NODE, SN_ServerLink); VALIDATE_SERVER_NODE(node1); bMainNode = IsMainNode (node1); // It may be deleted in // callback cur = cur->Blink; if (CallBackProc!=NULL) { res = (*CallBackProc) (CBParam, &node1->SN_Server, node1->SN_InterfaceIndex, node1->SN_Protocol, node1->SN_AdvertisingNode, node1->N_NodeFlags ); } } while (res==FALSE && !bMainNode); } // Call them with just best entry else if (CallBackProc!=NULL) { res = (*CallBackProc) (CBParam, &node->SN_Server, node->SN_InterfaceIndex, node->SN_Protocol, node->SN_AdvertisingNode, node->N_NodeFlags ); } if (res==-1) { if (bNeedHashLock) ReleaseServerTableList (&HashList->HL_List); else ReleaseServerTableList (enumNode->EN_ListLock); SetLastError (ERROR_GEN_FAILURE); return FALSE; } else if (bNeedHashLock) { ReleaseServerTableList (&HashList->HL_List); if (!AcquireServerTableList (enumNode->EN_ListLock, TRUE)) { SetLastError (ERROR_GEN_FAILURE); return FALSE; } } } // If enumerating through the change queue, this might be // the last who needs to know about deleted server entry, // so it will have to actually initiate deletion if ((Enumerator==ServerTable.ST_LastEnumerator) // make sure the node is still there && (enumNode->N_Links[SDB_CHANGE_QUEUE_LINK].Flink == &node->N_Links[SDB_CHANGE_QUEUE_LINK])) { if (IsEnumerator(node)) ServerTable.ST_LastEnumerator = (HANDLE)node; else if (node->SN_HopCount==IPX_MAX_HOP_COUNT) { ASSERTMSG ("Node being reset is not main ", IsMainNode (node)); ResetMainNode (node); RemoveEntryList (&node->N_Links[SDB_CHANGE_QUEUE_LINK]); InitializeListEntry (&node->N_Links[SDB_CHANGE_QUEUE_LINK]); ASSERTMSG ("Deleted node in change queue has subnodes ", !IsListEntry (&node->SN_ServerLink)); if (AcquireServerTableList (&ServerTable.ST_DeletedList, TRUE)) { InsertTailList (&ServerTable.ST_DeletedList.PL_Head, &node->SN_ServerLink); ServerTable.ST_DeletedListCnt += 1; if (ServerTable.ST_DeletedListCnt==SDBMaxUnsortedServers) UpdateSortedList (); ReleaseServerTableList (&ServerTable.ST_DeletedList); } // If we fail in locking we just let it hang around // (at least we won't risk damaging the list) } } } while (!res); ASSERT (res==TRUE); ReleaseServerTableList (enumNode->EN_ListLock); return TRUE; #undef enumNode } /*++ ******************************************************************* G e t O n e C B Routine Description: Callback proc for EnumerateServers. Copies the first entry with which it is called and stops enumeration by returning TRUE Arguments: CBParam - pointer to buffer to which to copy service info Server, InterfaceIndex, Protocol, AdvertisingNode - service data Flags - ignored Return Value: TRUE ******************************************************************* --*/ BOOL GetOneCB ( IN LPVOID CBParam, IN OUT PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { #define Service ((PIPX_SERVICE)CBParam) IpxServerCpy (&Service->Server, Server); Service->InterfaceIndex = InterfaceIndex; Service->Protocol = Protocol; return TRUE; #undef Service } /*++ ******************************************************************* D e l e t e A l l S e r v e r s C B Routine Description: Callback proc for EnumerateServers that deletes all server entries with which it is called Arguments: CBParam - enumeration handle that identifies enumeration Server - pointer to server data inside server node from which node itself is computed Return Value: FALSE - deletion succeded, continue TRUE - failure to lock SDB list, stop enumeration and return FALSE to client (error code is set in this routine) ******************************************************************* --*/ BOOL DeleteAllServersCB ( IN LPVOID CBParam, IN PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { PSERVER_NODE node = CONTAINING_RECORD (Server, SERVER_NODE, SN_Server); if (AcquireAllLocks ()) { node->SN_HopCount = IPX_MAX_HOP_COUNT; if (IsMainNode (node)) { if (IsListEmpty (&node->SN_ServerLink)) DeleteMainNode (node); else ChangeMainNode ( node, CONTAINING_RECORD ( node->SN_ServerLink.Flink, SERVER_NODE, SN_ServerLink), NULL); } else DeleteNode (node); ReleaseAllLocks (); return FALSE; } else { return -1; } } BOOL DeleteNonLocalServersCB ( IN LPVOID CBParam, IN PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { if (InterfaceIndex!=INTERNAL_INTERFACE_INDEX) return DeleteAllServersCB (CBParam, Server, InterfaceIndex, Protocol, AdvertisingNode, Flags); else return FALSE; } /*++ ******************************************************************* E n a b l e A l l S e r v e r s C B Routine Description: Callback proc for EnumerateServers that reenables all server entries with which it is called Arguments: CBParam - enumeration handle that identifies enumeration Server - pointer to server data inside server node from which node itself is computed Return Value: FALSE - deletion succeded, continue TRUE - failure to lock SDB list, stop enumeration and return FALSE to client (error code is set in this routine) ******************************************************************* --*/ BOOL EnableAllServersCB ( IN LPVOID CBParam, IN PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { PSERVER_NODE node = CONTAINING_RECORD (Server, SERVER_NODE, SN_Server); if (AcquireAllLocks ()) { if (IsDisabledNode (node)) { ResetDisabledNode (node); if (!IsMainNode (node)) { PSERVER_NODE node1 = node; do { node1 = CONTAINING_RECORD ( node1->SN_ServerLink.Blink, SERVER_NODE, SN_ServerLink); } while (!IsMainNode (node1) && (IsDisabledNode(node1) || (node1->SN_HopCount>node->SN_HopCount))); if (IsMainNode (node1) && (node1->SN_HopCount>node->SN_HopCount)) ChangeMainNode (node1, node, NULL); else { RemoveEntryList (&node->SN_ServerLink); InsertHeadList (&node1->SN_ServerLink, &node->SN_ServerLink); } } } ReleaseAllLocks (); return FALSE; } else { return -1; } } /*++ ******************************************************************* D i s a b l e A l l S e r v e r s C B Routine Description: Callback proc for EnumerateServers that disables all server entries with which it is called Arguments: CBParam - enumeration handle that identifies enumeration Server - pointer to server data inside server node from which node itself is computed Return Value: FALSE - deletion succeded, continue TRUE - failure to lock SDB list, stop enumeration and return FALSE to client (error code is set in this routine) ******************************************************************* --*/ BOOL DisableAllServersCB ( IN LPVOID CBParam, IN PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { PSERVER_NODE node = CONTAINING_RECORD (Server, SERVER_NODE, SN_Server); if (AcquireAllLocks ()) { if (!IsDisabledNode (node)) { SetDisabledNode (node); if (IsMainNode (node)) { if (!IsListEmpty (&node->SN_ServerLink)) { ChangeMainNode ( node, CONTAINING_RECORD ( node->SN_ServerLink.Flink, SERVER_NODE, SN_ServerLink), NULL); } } else { PSERVER_NODE node1 = node; do { node1 = CONTAINING_RECORD ( node1->SN_ServerLink.Blink, SERVER_NODE, SN_ServerLink); } while (!IsMainNode (node1) && !IsDisabledNode(node1)); RemoveEntryList (&node->SN_ServerLink); InsertTailList (&node1->SN_ServerLink, &node->SN_ServerLink); } } ReleaseAllLocks (); } else { return -1; } return NO_ERROR; } /*++ ******************************************************************* C o n v e r t T o S t a t i c C B Routine Description: Callback proc for EnumerateServers that converts all server entries with which it is called to static (changes protocol field to static) Arguments: CBParam - enumeration handle that identifies enumeration Server - pointer to server data inside server node from which node itself is computed Return Value: FALSE ******************************************************************* --*/ BOOL ConvertToStaticCB ( IN LPVOID CBParam, IN PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { #define enumNode ((PENUMERATOR_NODE)CBParam) PSERVER_NODE node = CONTAINING_RECORD (Server, SERVER_NODE, SN_Server); node->SN_Protocol = IPX_PROTOCOL_STATIC; IpxNodeCpy (node->SN_AdvertisingNode, IPX_BCAST_NODE); #undef enumNode return FALSE; } /*++ ******************************************************************* D e l e t e L i s t E n u m e r a t o r Routine Description: Releases resources associated with list enumerator (this includes server entries that are queued to change queue before being deleted) Arguments: Enumerator - handle obtained from CreateListEnumerator Return Value: None ******************************************************************* --*/ void DeleteListEnumerator ( IN HANDLE Enumerator ) { #define enumNode ((PENUMERATOR_NODE)Enumerator) if (!AcquireServerTableList (enumNode->EN_ListLock, TRUE)) return; VALIDATE_ENUMERATOR_NODE(Enumerator); if (Enumerator==ServerTable.ST_LastEnumerator) { // Release all servers marked for deletion PLIST_ENTRY cur = enumNode->N_Links[enumNode->EN_LinkIdx].Blink; // Reset to note that there are no enumerators and // nodes have to be deleted rigth away. ServerTable.ST_LastEnumerator = NULL; while (cur!=enumNode->EN_ListHead) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[enumNode->EN_LinkIdx]); VALIDATE_NODE(node); cur = cur->Blink; if (IsEnumerator (node)) { ServerTable.ST_LastEnumerator = (HANDLE)node; break; } else if (node->SN_HopCount==IPX_MAX_HOP_COUNT) { ASSERTMSG ("Node being reset is not main ", IsMainNode (node)); ResetMainNode (node); RemoveEntryList (&node->N_Links[SDB_CHANGE_QUEUE_LINK]); InitializeListEntry (&node->N_Links[SDB_CHANGE_QUEUE_LINK]); ASSERTMSG ("Deleted node in change queue has subnodes ", !IsListEntry (&node->SN_ServerLink)); if (AcquireServerTableList (&ServerTable.ST_DeletedList, TRUE)) { InsertTailList (&ServerTable.ST_DeletedList.PL_Head, &node->SN_ServerLink); ServerTable.ST_DeletedListCnt += 1; if (ServerTable.ST_DeletedListCnt==SDBMaxUnsortedServers) UpdateSortedList (); ReleaseServerTableList (&ServerTable.ST_DeletedList); } // If we fail in locking we just let it hang around // (at least we won't risk damaging the list) } } } RemoveEntryList (&enumNode->N_Links[enumNode->EN_LinkIdx]); if ((enumNode->EN_LinkIdx==SDB_INTF_LIST_LINK) && IsListEmpty (enumNode->EN_ListHead)) { PINTF_NODE intfNode = CONTAINING_RECORD ( enumNode->EN_ListHead, INTF_NODE, IN_Head); RemoveEntryList (&intfNode->IN_Link); GlobalFree (intfNode); } ReleaseServerTableList (enumNode->EN_ListLock); GlobalFree (Enumerator); #undef enumNode } /*++ ******************************************************************* G e t F i r s t S e r v e r Routine Description: Find and return first service in the order specified by the ordering method. Search is limited only to certain types of services as specified by the exclusion flags end corresponding fields in Server parameter. Returns ERROR_NO_MORE_ITEMS if there are no services in the table that meet specified criteria. Arguments: OrderingMethod - which ordering to consider in determining what is the first server ExclusionFlags - flags to limit search to certain servers according to specified criteria Server - On input: criteria for exclusion flags On output: first service entry in the specified order Return Value: NO_ERROR - server was found that meets specified criteria ERROR_NO_MORE_ITEMS - no server exist with specified criteria other - operation failed (windows error code) ******************************************************************* --*/ DWORD GetFirstServer ( IN DWORD OrderingMethod, IN DWORD ExclusionFlags, IN OUT PIPX_SERVER_ENTRY_P Server, IN OUT ULONG *InterfaceIndex, IN OUT ULONG *Protocol ) { DWORD status=NO_ERROR; PSDB_HASH_LIST HashList; PPROTECTED_LIST list; INT link; PLIST_ENTRY cur; switch (OrderingMethod) { case STM_ORDER_BY_TYPE_AND_NAME: break; case STM_ORDER_BY_INTERFACE_TYPE_NAME: if (!(ExclusionFlags & STM_ONLY_THIS_INTERFACE)) { if (!AcquireServerTableList (&ServerTable.ST_IntfList, TRUE)) return ERROR_GEN_FAILURE; if (IsListEmpty (&ServerTable.ST_IntfList.PL_Head)) { ReleaseServerTableList (&ServerTable.ST_IntfList); return ERROR_NO_MORE_ITEMS; } *InterfaceIndex = CONTAINING_RECORD ( ServerTable.ST_IntfList.PL_Head.Flink, INTF_NODE, IN_Link)->IN_InterfaceIndex; ReleaseServerTableList (&ServerTable.ST_IntfList); } break; default: ASSERTMSG ("Invalid ordering method specified ", FALSE); return ERROR_INVALID_PARAMETER; } if (ExclusionFlags & STM_ONLY_THIS_NAME) { HashList = &ServerTable.ST_HashLists[HashFunction (Server->Name)]; if (!AcquireServerTableList (&HashList->HL_List, TRUE)) return ERROR_GEN_FAILURE; list = &HashList->HL_List; link = SDB_HASH_TABLE_LINK; } else { if (ServerTable.ST_UpdatePending==-1) DoUpdateSortedList (); if (!AcquireServerTableList (&ServerTable.ST_SortedListPRM, TRUE)) return ERROR_GEN_FAILURE; list = &ServerTable.ST_SortedListPRM; link = SDB_SORTED_LIST_LINK; } cur = list->PL_Head.Flink; while (TRUE) { // We may need to loop through interface lists status = DoFindNextNode (cur, list, link, OrderingMethod==STM_ORDER_BY_INTERFACE_TYPE_NAME ? ExclusionFlags|STM_ONLY_THIS_INTERFACE : ExclusionFlags, Server, InterfaceIndex, Protocol, NULL ); // If looping through all interfaces in interface order and // no items are available, we may need to check another interface if ((status==ERROR_NO_MORE_ITEMS) && (OrderingMethod==STM_ORDER_BY_INTERFACE_TYPE_NAME) && !(ExclusionFlags&STM_ONLY_THIS_INTERFACE)) { if (!AcquireServerTableList (&ServerTable.ST_IntfList, TRUE)) { status = ERROR_GEN_FAILURE; break; } // Get next interface in interface list cur = ServerTable.ST_IntfList.PL_Head.Flink; while (cur!=&ServerTable.ST_IntfList.PL_Head) { PINTF_NODE intfNode = CONTAINING_RECORD (cur, INTF_NODE, IN_Link); if (*InterfaceIndexIN_InterfaceIndex) { *InterfaceIndex = intfNode->IN_InterfaceIndex; break; } cur = cur->Flink; } ReleaseServerTableList (&ServerTable.ST_IntfList); if (cur!=&ServerTable.ST_IntfList.PL_Head) { // Restart the search with another interface index cur = list->PL_Head.Flink; continue; } } break; } if (link==SDB_HASH_TABLE_LINK) ReleaseServerTableList (&HashList->HL_List); else /* if (link==SDB_SORTED_LIST_LINK) */ ReleaseServerTableList (&ServerTable.ST_SortedListPRM); return status; } /*++ ******************************************************************* G e t N e x t S e r v e r Routine Description: Find and return next service in the order specified by the ordering method. Search starts from specified service and is limited only to certain types of services as specified by the exclusion flags and corresponding fields in Server parameter. Arguments: OrderingMethod - which ordering to consider in determining what is the first server ExclusionFlags - flags to limit search to certain servers according to fields of Server Server - On input server entry from which to compute the next On output: first service entry in the specified order Return Value: NO_ERROR - server was found that meets specified criteria ERROR_NO_MORE_ITEMS - no server exist with specified criteria other - operation failed (windows error code) ******************************************************************* --*/ DWORD GetNextServer ( IN DWORD OrderingMethod, IN DWORD ExclusionFlags, IN OUT PIPX_SERVER_ENTRY_P Server, IN OUT ULONG *InterfaceIndex, IN OUT ULONG *Protocol ) { PLIST_ENTRY cur=NULL; PSERVER_NODE theNode=NULL; DWORD status=NO_ERROR; PSDB_HASH_LIST HashList; PPROTECTED_LIST list; INT link; INT res; switch (OrderingMethod) { case STM_ORDER_BY_TYPE_AND_NAME: break; case STM_ORDER_BY_INTERFACE_TYPE_NAME: break; default: ASSERTMSG ("Invalid ordering method specified ", FALSE); return ERROR_INVALID_PARAMETER; } if (!AcquireServerTableList (&ServerTable.ST_SortedListPRM, TRUE)) return ERROR_GEN_FAILURE; if (Server->Name[0]!=0) { HashList = &ServerTable.ST_HashLists[HashFunction (Server->Name)]; if (!AcquireServerTableList (&HashList->HL_List, TRUE)) { ReleaseServerTableList (&ServerTable.ST_SortedListPRM); return ERROR_GEN_FAILURE; } cur = HashList->HL_List.PL_Head.Flink; while (cur!=&HashList->HL_List.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[SDB_HASH_TABLE_LINK]); VALIDATE_NODE(node); if (!IsEnumerator(node) && (!IsDisabledNode (node) || ((ExclusionFlags & STM_ONLY_THIS_PROTOCOL) && (*Protocol==IPX_PROTOCOL_STATIC))) && (node->SN_Server.HopCountType == node->SN_Server.Type) { res = IpxNameCmp (Server->Name, node->SN_Server.Name); if ((res==0) && IsSortedNode (node)) { theNode = node; } else if (res<0) break; } else if (Server->TypeSN_Server.Type) break; } cur = cur->Flink; } if (ExclusionFlags&STM_ONLY_THIS_NAME) { ReleaseServerTableList (&ServerTable.ST_SortedListPRM); if (theNode!=NULL) { list = &HashList->HL_List; link = SDB_HASH_TABLE_LINK; } else { ReleaseServerTableList (&HashList->HL_List); return ERROR_NO_MORE_ITEMS; } } else { ReleaseServerTableList (&HashList->HL_List); goto DoHardWay; } } else { DoHardWay: list = &ServerTable.ST_SortedListPRM; link = SDB_SORTED_LIST_LINK; if (theNode!=NULL) cur = theNode->N_Links[SDB_SORTED_LIST_LINK].Flink; else { cur = ServerTable.ST_SortedListPRM.PL_Head.Flink; while (cur!=&ServerTable.ST_SortedListPRM.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[SDB_SORTED_LIST_LINK]); VALIDATE_NODE(node); if (!IsEnumerator(node) && (!IsDisabledNode (node) || ((ExclusionFlags & STM_ONLY_THIS_PROTOCOL) && (*Protocol==IPX_PROTOCOL_STATIC))) && (node->SN_Server.HopCountTypeSN_Server.Type) || ((Server->Type == node->SN_Server.Type) && (IpxNameCmp (Server->Name, node->SN_Server.Name)<0))) break; } cur = cur->Flink; } } } while (TRUE) { // We may need to loop through interface lists status = DoFindNextNode (cur, list, link, OrderingMethod==STM_ORDER_BY_INTERFACE_TYPE_NAME ? ExclusionFlags|STM_ONLY_THIS_INTERFACE : ExclusionFlags, Server, InterfaceIndex, Protocol, NULL ); // If looping through all interfaces in interface order and // no items are available, we may need to check another interface if ((status==ERROR_NO_MORE_ITEMS) && (OrderingMethod==STM_ORDER_BY_INTERFACE_TYPE_NAME) && !(ExclusionFlags&STM_ONLY_THIS_INTERFACE)) { if (!AcquireServerTableList (&ServerTable.ST_IntfList, TRUE)) { status = ERROR_GEN_FAILURE; break; } // Get next interface in interface list cur = ServerTable.ST_IntfList.PL_Head.Flink; while (cur!=&ServerTable.ST_IntfList.PL_Head) { PINTF_NODE intfNode = CONTAINING_RECORD (cur, INTF_NODE, IN_Link); if (*InterfaceIndexIN_InterfaceIndex) { *InterfaceIndex = intfNode->IN_InterfaceIndex; break; } cur = cur->Flink; } ReleaseServerTableList (&ServerTable.ST_IntfList); if (cur!=&ServerTable.ST_IntfList.PL_Head) { // Restart the search with another interface index cur = list->PL_Head.Flink; continue; } } break; } if (link==SDB_HASH_TABLE_LINK) ReleaseServerTableList (&HashList->HL_List); else /* if (link==SDB_SORTED_LIST_LINK) */ ReleaseServerTableList (&ServerTable.ST_SortedListPRM); return status; } /*++ ******************************************************************* G e t N e x t S e r v e r F r o m I D Routine Description: Find and return service that follows server with specified ID in the type.name order. Arguments: ObjectID - on input: id of server form which to start the search on output: id of returned server Type - if not 0xFFFF, search should be limited to only servers of specified type Server, Protocol, InterfaceIndex - buffer to put returned server info in Return Value: TRUE - server was found FALSE - search failed ******************************************************************* --*/ BOOL GetNextServerFromID ( IN OUT PULONG ObjectID, IN USHORT Type, OUT PIPX_SERVER_ENTRY_P Server, OUT PULONG InterfaceIndex OPTIONAL, OUT PULONG Protocol OPTIONAL ) { PSDB_HASH_LIST HashList; PLIST_ENTRY cur; PSERVER_NODE theNode = NULL; DWORD status=NO_ERROR; HashList = &ServerTable.ST_HashLists[(*ObjectID)%SDB_NAME_HASH_SIZE]; if (*ObjectID==SDB_INVALID_OBJECT_ID) { if (ServerTable.ST_UpdatePending==-1) DoUpdateSortedList (); } if (!AcquireServerTableList (&ServerTable.ST_SortedListPRM, TRUE)) return ERROR_GEN_FAILURE; if (*ObjectID!=SDB_INVALID_OBJECT_ID) { if (!AcquireServerTableList (&HashList->HL_List, TRUE)) { ReleaseServerTableList (&ServerTable.ST_SortedListPRM); SetLastError (ERROR_GEN_FAILURE); return FALSE; } cur = HashList->HL_List.PL_Head.Flink; while (cur!=&HashList->HL_List.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[SDB_HASH_TABLE_LINK]); VALIDATE_NODE(node); if (!IsEnumerator (node) && !IsDisabledNode (node) && (node->SN_HopCount < IPX_MAX_HOP_COUNT) && (node->SN_ObjectID == *ObjectID)) { theNode = node; break; } cur = cur->Flink; } ReleaseServerTableList (&HashList->HL_List); if (theNode==NULL) { ReleaseServerTableList (&ServerTable.ST_SortedListPRM); SetLastError (NO_ERROR); return FALSE; } else if (!IsSortedNode (theNode)) { cur = ServerTable.ST_SortedListPRM.PL_Head.Flink; while (cur!=&ServerTable.ST_SortedListPRM.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[SDB_SORTED_LIST_LINK]); VALIDATE_NODE(node); if (!IsEnumerator(node) && !IsDisabledNode (node)) { if ((theNode->SN_Server.TypeSN_Server.Type) || ((theNode->SN_Server.Type == node->SN_Server.Type) && (IpxNameCmp (theNode->SN_Server.Name, node->SN_Server.Name)<0))) break; } cur = cur->Flink; } } else cur = theNode->N_Links[SDB_SORTED_LIST_LINK].Flink; } else cur = ServerTable.ST_SortedListPRM.PL_Head.Flink; Server->Type = Type; status = DoFindNextNode (cur, &ServerTable.ST_SortedListPRM, SDB_SORTED_LIST_LINK, Type==0xFFFF ? 0 : STM_ONLY_THIS_TYPE, Server, InterfaceIndex, Protocol, ObjectID ); ReleaseServerTableList (&ServerTable.ST_SortedListPRM); return status == NO_ERROR; } /*++ ******************************************************************* D o F i n d N e x t N o d e Routine Description: Scan through SortedListPRM to find the first entry that matches specified cirteria. Permanent sorted list must be locked before calling this routine Arguments: cur - pointer to entry in SortedListPRM from which to start the search ExclusionFlags - flags to limit search to certain servers according to fields of Server Server, InterfaceIndex, Protocol - on input: search criteria on output: data of found server ObjectID - object ID of returned server Return Value: NO_ERROR - server was found that matches the criteria ERROR_NO_MORE_ITEMS - no server exist that matches criteria other - operation failed (windows error code) ******************************************************************* --*/ DWORD DoFindNextNode ( IN PLIST_ENTRY cur, IN PPROTECTED_LIST list, IN INT link, IN DWORD ExclusionFlags, IN OUT PIPX_SERVER_ENTRY_P Server, IN OUT PULONG InterfaceIndex OPTIONAL, IN OUT PULONG Protocol OPTIONAL, OUT PULONG ObjectID OPTIONAL ) { while (cur!=&list->PL_Head) { PSDB_HASH_LIST HashList; PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[link]); VALIDATE_NODE(node); if (!IsEnumerator(node) && (!IsDisabledNode (node) || ((ExclusionFlags & STM_ONLY_THIS_PROTOCOL) && (*Protocol==IPX_PROTOCOL_STATIC))) && (node->SN_Server.HopCountType>node->SN_Type) goto DoNextNode; else if (Server->TypeSN_Type) break; } if (ExclusionFlags & STM_ONLY_THIS_NAME) { INT res = IpxNameCmp (Server->Name,node->SN_Name); if (res>0) goto DoNextNode; else if (res<0) { if (ExclusionFlags & STM_ONLY_THIS_TYPE) break; else goto DoNextNode; } } HashList = node->SN_HashList; if (list!=&HashList->HL_List) { if (!AcquireServerTableList (&HashList->HL_List, TRUE)) return ERROR_GEN_FAILURE; } do { if ((ExclusionFlags & STM_ONLY_THIS_PROTOCOL|STM_ONLY_THIS_INTERFACE) == (STM_ONLY_THIS_PROTOCOL|STM_ONLY_THIS_INTERFACE)) { if ((*Protocol==node->SN_Protocol) && (*InterfaceIndex==node->SN_InterfaceIndex)) break; } else if (ExclusionFlags & STM_ONLY_THIS_PROTOCOL) { if (*Protocol==node->SN_Protocol) break; } else if (ExclusionFlags & STM_ONLY_THIS_INTERFACE) { if (*InterfaceIndex!=node->SN_InterfaceIndex) break; } else break; node = CONTAINING_RECORD (node->SN_ServerLink.Flink, SERVER_NODE, SN_ServerLink); VALIDATE_SERVER_NODE(node); if (cur==&node->N_Links[link]) { if (list!=&HashList->HL_List) ReleaseServerTableList (&HashList->HL_List); goto DoNextNode; } } while (1); IpxServerCpy (Server, &node->SN_Server); if (ARGUMENT_PRESENT (ObjectID)) { if (node->SN_ObjectID==SDB_INVALID_OBJECT_ID) node->SN_ObjectID = GenerateUniqueID (node->SN_HashList); *ObjectID = node->SN_ObjectID; } if (ARGUMENT_PRESENT (InterfaceIndex)) *InterfaceIndex = node->SN_InterfaceIndex; if (ARGUMENT_PRESENT (Protocol)) *Protocol = node->SN_Protocol; if (list!=&HashList->HL_List) ReleaseServerTableList (&HashList->HL_List); return NO_ERROR; } DoNextNode: cur = cur->Flink; } return ERROR_NO_MORE_ITEMS; } /*++ ******************************************************************* F i n d I n t f L i n k Routine Description: Find interface list given an interface index. Create new interface list if one for given index does not exist Interface list must be locked when calling this routine Arguments: InterfaceIndex - index to look for Return Value: Head of interface list (link at which new entry can be inserted) NULL if list could not be found and creation of new list failed ******************************************************************* --*/ PLIST_ENTRY FindIntfLink ( ULONG InterfaceIndex ) { PLIST_ENTRY cur; PINTF_NODE node; cur = ServerTable.ST_IntfList.PL_Head.Flink; while (cur!=&ServerTable.ST_IntfList.PL_Head) { node = CONTAINING_RECORD (cur, INTF_NODE, IN_Link); if (InterfaceIndex==node->IN_InterfaceIndex) return &node->IN_Head; else if (InterfaceIndexIN_InterfaceIndex) break; cur = cur->Flink; } node = (PINTF_NODE)GlobalAlloc (GMEM_FIXED, sizeof (INTF_NODE)); if (node==NULL) { Trace (DEBUG_FAILURES, "File: %s, line: %ld. Can't allocate interface list node (gle:%ld).", __FILE__, __LINE__, GetLastError ()); SetLastError (ERROR_NOT_ENOUGH_MEMORY); return NULL; } node->IN_InterfaceIndex = InterfaceIndex; InitializeListHead (&node->IN_Head); InsertTailList (cur, &node->IN_Link); return &node->IN_Head; } /*++ ******************************************************************* F i n d T y p e L i n k Routine Description: Find type list given a type value. Create new type list if one for given type does not exist Type list must be locked when calling this routine Arguments: Type - type to look for Return Value: Head of type list (link at which new entry can be inserted) NULL if list could not be found and creation of new list failed ******************************************************************* --*/ PLIST_ENTRY FindTypeLink ( USHORT Type ) { PLIST_ENTRY cur; PTYPE_NODE node; cur = ServerTable.ST_TypeList.PL_Head.Flink; while (cur!=&ServerTable.ST_TypeList.PL_Head) { node = CONTAINING_RECORD (cur, TYPE_NODE, TN_Link); if (Type==node->TN_Type) return &node->TN_Head; else if (TypeTN_Type) break; cur = cur->Flink; } node = (PTYPE_NODE)GlobalAlloc (GMEM_FIXED, sizeof (TYPE_NODE)); if (node==NULL) { Trace (DEBUG_FAILURES, "File: %s, line: %ld. Can't allocate type list node (gle:%ld).", __FILE__, __LINE__, GetLastError ()); SetLastError (ERROR_NOT_ENOUGH_MEMORY); return NULL; } node->TN_Type = Type; InitializeListHead (&node->TN_Head); InsertTailList (cur, &node->TN_Link); return &node->TN_Head; } /*++ ******************************************************************* F i n d S o r t e d L i n k Routine Description: Find place for server with given type and name in SortedListTMP If there is another node there with the same name and type it is removed from the list SortedListTMP must be locked when calling this routine Arguments: Type - type to look for Name - name to look for Return Value: Link in SortedListTMP at which server with given name and type should be inserted This routine can't fail ******************************************************************* --*/ PLIST_ENTRY FindSortedLink ( USHORT Type, PUCHAR Name ) { PLIST_ENTRY cur; INT res; cur = ServerTable.ST_SortedListTMP.PL_Head.Flink; while (cur!=&ServerTable.ST_SortedListTMP.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[SDB_SORTED_LIST_LINK]); VALIDATE_SERVER_NODE(node); if (Type==node->SN_Type) { res = IpxNameCmp (Name, node->SN_Name); if (res==0) { cur = cur->Flink; RemoveEntryList (&node->N_Links[SDB_SORTED_LIST_LINK]); InitializeListEntry (&node->N_Links[SDB_SORTED_LIST_LINK]); ServerTable.ST_TMPListCnt -= 1; break; } else if (res<0) break; } else if (TypeSN_Type) break; cur = cur->Flink; } return cur; } /*++ ******************************************************************* H a s h F u n c t i o n Routine Description: Computes hash function for given server name. In addition it normalizes length and capitalization of name Arguments: Name - name to process Return Value: Hash value ******************************************************************* --*/ INT HashFunction ( PUCHAR Name ) { INT i; INT res = 0; for (i=0; i<47; i++) { Name[i] = (UCHAR)toupper(Name[i]); if (Name[i]==0) break; else res += Name[i]; } if ((i==47) && (Name[i]!=0)) { Trace (DEBUG_SERVERDB, "Correcting server name: %.48s.", Name); Name[i] = 0; } return res % SDB_NAME_HASH_SIZE; } /*++ ******************************************************************* G e n e r a t e U n i q u e I D Routine Description: Generates "unique" ULONG for server by combining hash bucket number and unique ID of entry in hash list. The number is kept with entry until there is a danger of collision due to number wraparound Arguments: HashList - hash bucket to generate ID for Return Value: ULONG ID ******************************************************************* --*/ ULONG GenerateUniqueID ( PSDB_HASH_LIST HashList ) { ULONG id = HashList->HL_ObjectID; HashList->HL_ObjectID = (HashList->HL_ObjectID+SDB_NAME_HASH_SIZE)&SDB_OBJECT_ID_MASK; // Make sure we won't assign invalid id if (HashList->HL_ObjectID==SDB_INVALID_OBJECT_ID) HashList->HL_ObjectID+=SDB_NAME_HASH_SIZE; // Create guard zone by invalidating all ID's that are one zone // above the zone we just entered if (!IsSameObjectIDZone(id, HashList->HL_ObjectID)) { PLIST_ENTRY cur = HashList->HL_List.PL_Head.Flink; ULONG oldMask = (HashList->HL_ObjectID & SDB_OBJECT_ID_ZONE_MASK) + SDB_OBJECT_ID_ZONE_UNIT; while (cur!=&HashList->HL_List.PL_Head) { PSERVER_NODE node = CONTAINING_RECORD (cur, SERVER_NODE, N_Links[SDB_HASH_TABLE_LINK]); if (!IsEnumerator(node)) { if ((node->SN_ObjectID & SDB_OBJECT_ID_ZONE_MASK)==oldMask) node->SN_ObjectID = SDB_INVALID_OBJECT_ID; } } cur = cur->Flink; } return id; }