/*++ Copyright (c) 1995 Microsoft Corporation Module Name: net\routing\ipx\sap\workers.c Abstract: This module implement all SAP agent work items Author: Vadim Eydelman 05-15-1995 Revision History: --*/ #include "sapp.h" // Max number of pending recv work items LONG MaxUnprocessedRequests=SAP_MAX_UNPROCESSED_REQUESTS_DEF; // Minimum number of queued recv requests LONG MinPendingRequests = SAP_MIN_REQUESTS_DEF; // How often to check on pending triggered update ULONG TriggeredUpdateCheckInterval=SAP_TRIGGERED_UPDATE_CHECK_INTERVAL_DEF; // How many requests to send if no response received within check interval ULONG MaxTriggeredUpdateRequests=SAP_MAX_TRIGGERED_UPDATE_REQUESTS_DEF; // Whether to respond for internal servers that are not registered with SAP // through the API calls (for standalone service only) ULONG RespondForInternalServers=SAP_RESPOND_FOR_INTERNAL_DEF; // Delay in response to general reguests for specific server type // if local servers are included in the packet ULONG DelayResponseToGeneral=SAP_DELAY_RESPONSE_TO_GENERAL_DEF; // Delay in sending change broadcasts if packet is not full ULONG DelayChangeBroadcast=SAP_DELAY_CHANGE_BROADCAST_DEF; UCHAR IPX_BCAST_NODE[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; UCHAR IPX_SAP_SOCKET[2] = {0x04, 0x52}; UCHAR IPX_INVALID_NET[4] = {0}; UCHAR IPX_INVALID_NODE[6] = {0}; #define SetupIpxSapPacket(packet,oper,net,node,sock) \ (packet)->PacketType = IPX_SAP_PACKET_TYPE; \ PUTUSHORT (oper, &(packet)->Operation); \ IpxNetCpy ((packet)->Dst.Network, net); \ IpxNodeCpy ((packet)->Dst.Node, node); \ IpxSockCpy ((packet)->Dst.Socket, sock); typedef struct _WORKER_QUEUE { LONG WQ_WorkerCount; LONG WQ_RequestQuota; LONG WQ_RequestExtra; LIST_ENTRY WQ_Queue; HANDLE WQ_Heap; HANDLE WQ_DoneEvent; HANDLE WQ_RecvEvent; } WORKER_QUEUE, *PWORKER_QUEUE; WORKER_QUEUE WorkerQueue; // Work item that obtains and processes SAP requests typedef struct _REQ_ITEM { IO_WORKER iow; SAP_BUFFER packet; } REQ_ITEM, *PREQ_ITEM; // Work item that generates responses for SAP general requests typedef struct _RESP_ITEM { PINTERFACE_DATA intf; USHORT svrType; // Type of servers requested BOOLEAN bcast; // Is destination a broadcast // address ? HANDLE hEnum; // Enumeration handle to keep // track of sent servers IO_WORKER iow; TIMER_WORKER tmw; SAP_BUFFER packet; } RESP_ITEM, *PRESP_ITEM; // Work item that generates responses for SAP GETNEAREST requests typedef struct _GNEAR_ITEM { PINTERFACE_DATA intf; IO_WORKER iow; SAP_BUFFER packet; } GNEAR_ITEM, *PGNEAR_ITEM; // Work item that sends SAP general requests typedef struct _SREQ_ITEM { PINTERFACE_DATA intf; IO_WORKER iow; SAP_BUFFER packet; } SREQ_ITEM, *PSREQ_ITEM; // Work item that generates periodic and change broadcasts typedef struct _BCAST_ITEM { IO_WORKER iow; TIMER_WORKER tmw; PINTERFACE_DATA intf; DWORD nextBcast; // Time (windows time in msec) for // next broadcast INT delayedSvrCount; // Number of servers already // in the packet that were delayed because // packet wasn't full DWORD delayedSendTime; // Time until packet was delayed // because it wasn't full HANDLE chngEnum; // Enumeration hadnle that keeps // track of chnagd servers HANDLE perdEnum; // Enumeration handle that keeps // track of servers during periodic // broadcasts SAP_BUFFER packet; } BCAST_ITEM, *PBCAST_ITEM; // Work item that gets and processes LPC requests typedef struct _LPC_ITEM { LPC_WORKER lpcw; NWSAP_REQUEST_MESSAGE request; } LPC_ITEM, *PLPC_ITEM; typedef struct _TREQ_ITEM { IO_WORKER iow; TIMER_WORKER tmw; AR_PARAM_BLOCK ar; PINTERFACE_DATA intf; ULONG pktCount; ULONG resend; ULONG listenSave; ULONG intervalSave; SAP_BUFFER packet; } TREQ_ITEM, *PTREQ_ITEM; typedef union _WORK_ITEM { REQ_ITEM req; LPC_ITEM lpc; BCAST_ITEM bcast; SREQ_ITEM sreq; TREQ_ITEM treq; GNEAR_ITEM gnear; RESP_ITEM resp; } WORK_ITEM, *PWORK_ITEM; // Parameter block passed to enumeration call back filter procedures typedef struct _GR_FILTER_PARAMS { INT svrIdx; // Index of server info in SAP packet BOOLEAN localSvr; // Local server included in the packet USHORT localHopCount; // Hop count used to track servers local // to the interface that may prevent use from broad- // casting info of same server obtained on different // interface PINTERFACE_DATA intf; // Pointer to interface data PSAP_BUFFER packet; // Packet to be filled } GR_FILTER_PARAMS, *PGR_FILTER_PARAMS; typedef struct _GN_FILTER_PARAMS { BOOLEAN found; // flag indicating the one server was found USHORT localHopCount; // Hop count used to track servers local // to the interface that may prevent use from broad- // casting info of same server obtained on different // interface PINTERFACE_DATA intf; // Pointer to interface data PSAP_BUFFER packet; // Packet to be filled } GN_FILTER_PARAMS, *PGN_FILTER_PARAMS; // Parameters used to construct a list of servers that // need their internal network information updated. typedef struct _SERVER_INTERNAL_UPDATE_NODE { ULONG InterfaceIndex; ULONG Protocol; PUCHAR AdvertisingNode; INT Flags; IPX_SERVER_ENTRY_P Server; struct _SERVER_INTERNAL_UPDATE_NODE * pNext; } SERVER_INTERNAL_UPDATE_NODE; #define AllocateWorker(worker,type) \ ((worker=(type *)HeapAlloc(WorkerQueue.WQ_Heap, 0, sizeof (WORK_ITEM))) \ ? InterlockedIncrement (&WorkerQueue.WQ_WorkerCount) \ : 0) #define DeallocateWorker(worker) { \ HeapFree (WorkerQueue.WQ_Heap, 0, worker); \ if (InterlockedDecrement (&WorkerQueue.WQ_WorkerCount)<0) { \ BOOL res = SetEvent (WorkerQueue.WQ_DoneEvent); \ ASSERTERRMSG ("Can't set workers done event ", res); \ } \ } // Local prototypes VOID APIENTRY ProcessReqItem ( PVOID worker ); VOID APIENTRY ProcessRespIOItem ( PVOID worker ); VOID APIENTRY ProcessRespTMItem ( PVOID worker ); VOID APIENTRY ProcessGnearItem ( PVOID worker ); VOID APIENTRY ProcessBcastIOItem ( PVOID worker ); VOID APIENTRY ProcessBcastTMItem ( PVOID worker ); VOID APIENTRY ProcessShutItem ( PVOID worker ); VOID APIENTRY ProcessSreqItem ( PVOID worker ); VOID APIENTRY ProcessSreqItem ( PVOID worker ); VOID APIENTRY ProcessLPCItem ( PVOID worker ); VOID APIENTRY ProcessTreqIOItem ( PVOID worker ); VOID APIENTRY ProcessTreqTMItem ( PVOID worker ); VOID APIENTRY FreeTreqItem ( PAR_PARAM_BLOCK rslt ); /*++ ******************************************************************* I n i t i a l i z e W o r k e r s Routine Description: Initialize heap to be used for allocation of work items Arguments: None Return Value: NO_ERROR - heap was initialized OK other - operation failed (windows error code) ******************************************************************* --*/ DWORD InitializeWorkers ( HANDLE RecvEvent ) { DWORD status; if (MaxUnprocessedRequests<(MinPendingRequests*11/10)) { MaxUnprocessedRequests = MinPendingRequests*11/10; Trace (DEBUG_FAILURES, "Set "SAP_MAX_UNPROCESSED_REQUESTS_STR" to %d (10%% above " SAP_MIN_REQUESTS_STR")", MaxUnprocessedRequests); } WorkerQueue.WQ_WorkerCount = 0; WorkerQueue.WQ_RequestQuota = MaxUnprocessedRequests; WorkerQueue.WQ_RequestExtra = -MinPendingRequests; WorkerQueue.WQ_DoneEvent = NULL; WorkerQueue.WQ_RecvEvent = RecvEvent; WorkerQueue.WQ_Heap = HeapCreate (0, 0, 0); if (WorkerQueue.WQ_Heap!=NULL) { return NO_ERROR; } else { status = GetLastError (); Trace (DEBUG_FAILURES, "File: %s, line %ld. Could not allocate worker's heap (gle:%ld).", __FILE__, __LINE__, status); } return status; } /*++ ******************************************************************* S h u t d o w n W o r k e r s Routine Description: Stops new worker creation and signals event when all workers are deleted Arguments: doneEvent - event to be signalled when all workers are deleted Return Value: None ******************************************************************* --*/ VOID ShutdownWorkers ( IN HANDLE doneEvent ) { WorkerQueue.WQ_DoneEvent = doneEvent; if (InterlockedDecrement (&WorkerQueue.WQ_WorkerCount)<0) { BOOL res = SetEvent (WorkerQueue.WQ_DoneEvent); ASSERTERRMSG ("Can't set workers done event ", res); } } /*++ ******************************************************************* D e l e t e W o r k e r s Routine Description: Deletes heap used for work items (and thus all work items as well) Arguments: None Return Value: None ******************************************************************* --*/ VOID DeleteWorkers ( void ) { ASSERT (WorkerQueue.WQ_WorkerCount==-1); HeapDestroy (WorkerQueue.WQ_Heap); } /*++ ******************************************************************* H o p C o u n t F i l t e r Routine Description: Server enumeration callback proc that filters out servers with high hop (<=15 for same interface, <15 for rest) Arguments: CBParam - enumeration callback parameter (param block above) Server, InterfaceIndex, Protocol, AdvertisingNode, Flags - server info Return Value: TRUE (to stop enumeration) when sap packet gets filled up, FALSE otherwise ******************************************************************* --*/ BOOL HopCountFilter ( IN LPVOID CBParam, IN PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { #define params ((PGR_FILTER_PARAMS)CBParam) ASSERTMSG ("To many servers ", params->svrIdxNetwork, IPX_INVALID_NET)!=0) && ((Protocol==IPX_PROTOCOL_LOCAL) || Routing || RespondForInternalServers)) { if ((Server->HopCountintf->index) && (Server->HopCountintf->filterOut==SAP_DONT_FILTER) || ((params->intf->filterOut==SAP_FILTER_PERMIT) && Filter (FILTER_TYPE_SUPPLY, params->intf->index, Server->Type, Server->Name)) || ((params->intf->filterOut==SAP_FILTER_DENY) && !Filter (FILTER_TYPE_SUPPLY, params->intf->index, Server->Type, Server->Name))) { IpxServerCpy (¶ms->packet->Entries[params->svrIdx], Server); PUTUSHORT (Server->Type, ¶ms->packet->Entries[params->svrIdx].Type); if (InterfaceIndex==params->intf->index) { PUTUSHORT (Server->HopCount, ¶ms->packet->Entries[params->svrIdx].HopCount); } else { PUTUSHORT (Server->HopCount+1, ¶ms->packet->Entries[params->svrIdx].HopCount); } params->svrIdx += 1; if (InterfaceIndex==INTERNAL_INTERFACE_INDEX) params->localSvr = TRUE; // Trace (DEBUG_ENTRIES, "\tAdding server, type: %04x, name: %.48s, hops: %d.", // Server->Type, Server->Name, Server->HopCount); if (params->svrIdx>=IPX_SAP_MAX_ENTRY) return TRUE; } } } #undef params return FALSE; } /*++ ******************************************************************* S p l i t H o r i z o n H o p C o u n t F i l t e r Routine Description: Server enumeration callback proc that filters out servers with high hop (<15) and applies split horizon algorithm based on interface index Arguments: CBParam - enumeration callback parameter (param block above) Server, InterfaceIndex, Protocol, AdvertisingNode, Flags - server info Return Value: TRUE (to stop enumeration) when sap packet gets filled up, FALSE otherwise ******************************************************************* --*/ BOOL SplitHorizonHopCountFilter ( IN LPVOID CBParam, IN OUT PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { #define params ((PGR_FILTER_PARAMS)CBParam) ASSERTMSG ("To many servers ", params->svrIdxNetwork, IPX_INVALID_NET)!=0) && ((Protocol==IPX_PROTOCOL_LOCAL) || Routing || RespondForInternalServers)) && (InterfaceIndex!=params->intf->index) && (Server->HopCountlocalHopCount) && ((params->intf->filterOut==SAP_DONT_FILTER) || ((params->intf->filterOut==SAP_FILTER_PERMIT) && Filter (FILTER_TYPE_SUPPLY, params->intf->index, Server->Type, Server->Name)) || ((params->intf->filterOut==SAP_FILTER_DENY) && !Filter (FILTER_TYPE_SUPPLY, params->intf->index, Server->Type, Server->Name)))) { IpxServerCpy (¶ms->packet->Entries[params->svrIdx], Server); PUTUSHORT (Server->Type, ¶ms->packet->Entries[params->svrIdx].Type); PUTUSHORT (Server->HopCount+1, ¶ms->packet->Entries[params->svrIdx].HopCount); if (InterfaceIndex==INTERNAL_INTERFACE_INDEX) params->localSvr = TRUE; params->svrIdx += 1; // Trace (DEBUG_ENTRIES, "\tAdding server, type: %04x, name: %.48s, hops: %d.", // Server->Type, Server->Name, Server->HopCount); if (params->svrIdx>=IPX_SAP_MAX_ENTRY) return TRUE; } // Make sure we won't send deleted servers params->localHopCount = IPX_MAX_HOP_COUNT-1; } else if (InterfaceIndex==params->intf->index) { params->localHopCount = Server->HopCount; // Remember hop count of entry // on local interface // Trace (DEBUG_ENTRIES, "\tBackup server entry, type: %04x, name: %.48s, hops: %d.", // Server->Type, Server->Name, Server->HopCount); } #undef params return FALSE; } /*++ ******************************************************************* S p l i t H o r i z o n F i l t e r Routine Description: Server enumeration callback proc that applies split horizon algorithm based on interface index to filter out enumerated servers Arguments: CBParam - enumeration callback parameter (param block above) Server, InterfaceIndex, Protocol, AdvertisingNode, Flags - server info Return Value: TRUE (to stop enumeration) when sap packet gets filled up, FALSE otherwise ******************************************************************* --*/ BOOL SplitHorizonFilter ( IN LPVOID CBParam, IN OUT PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { #define params ((PGR_FILTER_PARAMS)CBParam) ASSERTMSG ("To many servers ", params->svrIdxNetwork, IPX_INVALID_NET)!=0) && ((Protocol==IPX_PROTOCOL_LOCAL) || Routing || RespondForInternalServers)) || (InterfaceIndex!=params->intf->index)) && (Server->HopCountlocalHopCount) && ((params->intf->filterOut==SAP_DONT_FILTER) || ((params->intf->filterOut==SAP_FILTER_PERMIT) && Filter (FILTER_TYPE_SUPPLY, params->intf->index, Server->Type, Server->Name)) || ((params->intf->filterOut==SAP_FILTER_DENY) && !Filter (FILTER_TYPE_SUPPLY, params->intf->index, Server->Type, Server->Name))) ){ IpxServerCpy (¶ms->packet->Entries[params->svrIdx], Server); PUTUSHORT (Server->Type, ¶ms->packet->Entries[params->svrIdx].Type); if ((Server->HopCountHopCount+1, ¶ms->packet->Entries[params->svrIdx].HopCount); } else { PUTUSHORT (IPX_MAX_HOP_COUNT, ¶ms->packet->Entries[params->svrIdx].HopCount); } if (InterfaceIndex==INTERNAL_INTERFACE_INDEX) params->localSvr = TRUE; params->svrIdx += 1; // Trace (DEBUG_ENTRIES, "\tAdding server, type: %04x, name: %.48s, hops: %d.", // Server->Type, Server->Name, Server->HopCount); if (params->svrIdx>=IPX_SAP_MAX_ENTRY) return TRUE; } params->localHopCount = IPX_MAX_HOP_COUNT+1; } else if ((InterfaceIndex==params->intf->index) && !(Flags&SDB_DISABLED_NODE_FLAG)) { params->localHopCount = Server->HopCount; // Trace (DEBUG_ENTRIES,"\tBackup server entry, type: %04x, name: %.48s, hops: %d.", // Server->Type, Server->Name, Server->HopCount); } #undef params return FALSE; } /*++ ******************************************************************* S p l i t H o r i z o n F i l t e r Routine Description: Server enumeration callback proc that applies split horizon algorithm based on interface index to filter out enumerated servers and only gets deleted servers (HopCount=16) Arguments: CBParam - enumeration callback parameter (param block above) Server, InterfaceIndex, Protocol, AdvertisingNode, Flags - server info Return Value: TRUE (to stop enumeration) when sap packet gets filled up, FALSE otherwise ******************************************************************* --*/ BOOL SplitHorizonDeletedFilter ( IN LPVOID CBParam, IN OUT PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { #define params ((PGR_FILTER_PARAMS)CBParam) ASSERTMSG ("To many servers ", params->svrIdxHopCount>=IPX_MAX_HOP_COUNT) || (Flags&SDB_DISABLED_NODE_FLAG)) && (((InterfaceIndex==INTERNAL_INTERFACE_INDEX) && (IpxNetCmp (Server->Network, IPX_INVALID_NET)!=0) && ((Protocol==IPX_PROTOCOL_LOCAL) || Routing || RespondForInternalServers)) || (InterfaceIndex!=params->intf->index)) && ((params->intf->filterOut==SAP_DONT_FILTER) || ((params->intf->filterOut==SAP_FILTER_PERMIT) && Filter (FILTER_TYPE_SUPPLY, params->intf->index, Server->Type, Server->Name)) || ((params->intf->filterOut==SAP_FILTER_DENY) && !Filter (FILTER_TYPE_SUPPLY, params->intf->index, Server->Type, Server->Name))) ){ IpxServerCpy (¶ms->packet->Entries[params->svrIdx], Server); PUTUSHORT (Server->Type, ¶ms->packet->Entries[params->svrIdx].Type); PUTUSHORT (IPX_MAX_HOP_COUNT, ¶ms->packet->Entries[params->svrIdx].HopCount); params->svrIdx += 1; // Trace (DEBUG_ENTRIES, "\tAdding server, type: %04x, name: %.48s, hops: %d.", // Server->Type, Server->Name, Server->HopCount); if (params->svrIdx>=IPX_SAP_MAX_ENTRY) return TRUE; } } #undef params return FALSE; } /*++ ******************************************************************* G e t N e a r e s t F i l t e r Routine Description: Server enumeration callback proc that gets internal server if there is one or server with lowest hop count not on the local interface Arguments: CBParam - enumeration callback parameter (param block above) Server, InterfaceIndex, Protocol, AdvertisingNode, Flags - server info Return Value: TRUE (to stop enumeration) when it finds first internal server, FALSE otherwise ******************************************************************* --*/ BOOL GetNearestFilter ( IN LPVOID CBParam, IN OUT PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { #define params ((PGN_FILTER_PARAMS)CBParam) if (((InterfaceIndex==INTERNAL_INTERFACE_INDEX) && !(Flags&SDB_DONT_RESPOND_NODE_FLAG) && (IpxNetCmp (Server->Network, IPX_INVALID_NET)!=0) && ((Protocol==IPX_PROTOCOL_LOCAL) || Routing || RespondForInternalServers)) || ((InterfaceIndex!=params->intf->index) && (Server->HopCountlocalHopCount))) { if ((params->intf->filterOut==SAP_DONT_FILTER) || ((params->intf->filterOut==SAP_FILTER_PERMIT) && Filter (FILTER_TYPE_SUPPLY, params->intf->index, Server->Type, Server->Name)) || ((params->intf->filterOut==SAP_FILTER_DENY) && !Filter (FILTER_TYPE_SUPPLY, params->intf->index, Server->Type, Server->Name))) { IpxServerCpy (¶ms->packet->Entries[0], Server); PUTUSHORT (Server->Type, ¶ms->packet->Entries[0].Type); PUTUSHORT (Server->HopCount+1, ¶ms->packet->Entries[0].HopCount); // Trace (DEBUG_ENTRIES, "\tGetting server, type: %04x, name: %.48s, hops: %d.", // Server->Type, Server->Name, Server->HopCount); params->found = TRUE; params->localHopCount = Server->HopCount; if (InterfaceIndex==INTERNAL_INTERFACE_INDEX) return TRUE; } } #undef params return FALSE; } /*++ ******************************************************************* C o u n t S e r v e r s F i l t e r Routine Description: Server enumeration callback proc that count servers with which it is called back Arguments: CBParam - pointer to counter Server, InterfaceIndex, Protocol, AdvertisingNode, Flags - server info Return Value: FALSE to tell SDB to continue enumeration ******************************************************************* --*/ BOOL CountServersFilter ( IN LPVOID CBParam, IN OUT PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags ) { #define count ((ULONG *)CBParam) if (Protocol==IPX_PROTOCOL_SAP) *count += 1; return FALSE; #undef count } VOID AddRecvRequests ( LONG count ) { InterlockedExchangeAdd (&WorkerQueue.WQ_RequestQuota, count); if (InterlockedExchangeAdd (&WorkerQueue.WQ_RequestExtra, (-count))(-count)) { BOOL res = ResetEvent (WorkerQueue.WQ_RecvEvent); ASSERTMSG ("Could not reset recv event ", res); } } /*++ ******************************************************************* I n i t R e q I t e m Routine Description: Allocate and initialize IO request item Enqueue the request Arguments: None Return Value: NO_ERROR - item was initialized and enqueued OK other - operation failed (windows error code) ******************************************************************* --*/ DWORD InitReqItem ( VOID ) { PREQ_ITEM reqItem; do { if (InterlockedDecrement (&WorkerQueue.WQ_RequestQuota)<0) { InterlockedIncrement (&WorkerQueue.WQ_RequestQuota); return NO_ERROR; } if (!AllocateWorker (reqItem, REQ_ITEM)) { Trace (DEBUG_FAILURES, "File: %s, line %ld. Could not allocate request item (gle:%ld).", __FILE__, __LINE__, GetLastError ()); InterlockedIncrement (&WorkerQueue.WQ_RequestQuota); return ERROR_NOT_ENOUGH_MEMORY; } reqItem->iow.worker = ProcessReqItem; reqItem->iow.io.buffer = (PUCHAR)&reqItem->packet; reqItem->iow.io.cbBuffer = sizeof (reqItem->packet); Trace (DEBUG_REQ, "Generated receive request: %08lx.", reqItem); EnqueueRecvRequest (&reqItem->iow.io); } while (InterlockedIncrement (&WorkerQueue.WQ_RequestExtra)<0); return NO_ERROR; } /*++ ******************************************************************* P r o c e s s R e q I t e m Routine Description: Process received request Arguments: worker - pointer to work item to process Return Value: None ******************************************************************* --*/ VOID APIENTRY ProcessReqItem ( PVOID worker ) { PREQ_ITEM reqItem = CONTAINING_RECORD (worker, REQ_ITEM, iow.worker); INT i; PINTERFACE_DATA intf; LONG count; Trace (DEBUG_REQ, "Processing received request item %08lx on adpt: %d.", reqItem, reqItem->iow.io.adpt); count = InterlockedDecrement (&WorkerQueue.WQ_RequestExtra); if ((OperationalState==OPER_STATE_UP) && (reqItem->iow.io.status==NO_ERROR)) { if (count<0) { BOOL res = SetEvent (WorkerQueue.WQ_RecvEvent); ASSERTMSG ("Failed to set recv event ", res); } intf = GetInterfaceReference (reqItem->iow.io.adpt); if (intf!=NULL) { PSAP_BUFFER packet = &reqItem->packet; if ((IpxNodeCmp (packet->Src.Node, intf->adapter.LocalNode)!=0) || (IpxSockCmp (packet->Src.Socket, IPX_SAP_SOCKET)!=0) || (IpxNetCmp (packet->Src.Network, intf->adapter.Network)!=0)) { InterlockedIncrement (&intf->stats.SapIfInputPackets); packet->Length = GETUSHORT(&packet->Length); if (packet->Length>reqItem->iow.io.cbBuffer) packet->Length = (USHORT)reqItem->iow.io.cbBuffer; if (reqItem->iow.io.status==NO_ERROR) { if (packet->Length >= (FIELD_OFFSET (SAP_BUFFER, Operation) +sizeof(packet->Operation))) { packet->Operation = GETUSHORT(&packet->Operation); switch (packet->Operation) { case SAP_GENERAL_REQ: Trace (DEBUG_REQ, "\tGeneral request received for type: %04x.", GETUSHORT (&packet->Entries[0].Type)); if (intf->info.Supply==ADMIN_STATE_ENABLED) { PIPX_SERVER_ENTRY_P pEntry = packet->Entries; if (packet->Length >= (FIELD_OFFSET ( SAP_BUFFER, Entries[0].Type) +sizeof (pEntry->Type))) { pEntry->Type = GETUSHORT (&pEntry->Type); InitRespItem (intf, pEntry->Type, &packet->Src, memcmp ( packet->Dst.Node, IPX_BCAST_NODE, sizeof (IPX_BCAST_NODE)) ==0); } } break; case SAP_GENERAL_RESP: Trace (DEBUG_REQ, "\tGeneral response received."); if (intf->info.Listen==ADMIN_STATE_ENABLED) { PIPX_SERVER_ENTRY_P pEntry = packet->Entries; for (i=0; (iLength); i++,pEntry++) { pEntry->Type = GETUSHORT (&pEntry->Type); if ((intf->filterIn==SAP_DONT_FILTER) || ((intf->filterIn==SAP_FILTER_PERMIT) && Filter (FILTER_TYPE_LISTEN, intf->index, pEntry->Type, pEntry->Name)) || ((intf->filterIn==SAP_FILTER_DENY) && !Filter (FILTER_TYPE_LISTEN, intf->index, pEntry->Type, pEntry->Name))) { if ((IpxNodeCmp (packet->Src.Node, intf->adapter.LocalNode)!=0) || (IpxNetCmp (packet->Src.Network, intf->adapter.Network)!=0)) { USHORT Metric; if (GetServerMetric (pEntry, &Metric)==NO_ERROR) pEntry->HopCount = GETUSHORT (&pEntry->HopCount); else pEntry->HopCount = IPX_MAX_HOP_COUNT; UpdateServer ( pEntry, intf->index, IPX_PROTOCOL_SAP, (intf->info.PeriodicUpdateInterval!=MAXULONG) ? (intf->info.PeriodicUpdateInterval *intf->info.AgeIntervalMultiplier) : INFINITE, packet->Src.Node, 0, NULL ); } else { if (GETUSHORT (&pEntry->HopCount)HopCount = 0; else pEntry->HopCount = IPX_MAX_HOP_COUNT; IpxNetCpy (pEntry->Network, INTERNAL_IF_NET); IpxNodeCpy (pEntry->Node, INTERNAL_IF_NODE); UpdateServer ( pEntry, INTERNAL_INTERFACE_INDEX, IPX_PROTOCOL_SAP, ServerAgingTimeout*60, IPX_BCAST_NODE, 0, NULL ); } // Trace (DEBUG_ENTRIES, "\tInserting server," // " type: %04x, hops: %d, name: %.48s.", // pEntry->Type, // pEntry->HopCount, // pEntry->Name); if (((intf->stats.SapIfOperState!=OPER_STATE_UP) || (OperationalState!=OPER_STATE_UP)) && (IpxNetCmp (pEntry->Network, INTERNAL_IF_NET)!=0)) { pEntry->HopCount = IPX_MAX_HOP_COUNT; UpdateServer ( pEntry, intf->index, IPX_PROTOCOL_SAP, INFINITE, packet->Src.Node, 0, NULL ); break; } } // End if filter path } // end for } // end if Listening break; case SAP_GET_NEAREST_REQ: Trace (DEBUG_REQ, "\tGet nearest server request received."); if (intf->info.GetNearestServerReply==ADMIN_STATE_ENABLED) { PIPX_SERVER_ENTRY_P pEntry = packet->Entries; if (packet->Length >= (FIELD_OFFSET ( SAP_BUFFER, Entries[0].Type) +sizeof (pEntry->Type))) { pEntry->Type = GETUSHORT (&pEntry->Type); InitGnearItem (intf, pEntry->Type, &packet->Src); } } break; case SAP_GET_NEAREST_RESP: Trace (DEBUG_FAILURES, "\tGet nearest server response received" " from %.2x%.2x%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x.%.2x%.2x" " (I never ask for it).", packet->Src.Network[0], packet->Src.Network[1], packet->Src.Network[2], packet->Src.Network[3], packet->Src.Node[0], packet->Src.Node[1], packet->Src.Node[2], packet->Src.Node[3], packet->Src.Node[4], packet->Src.Node[5], packet->Src.Socket[0], packet->Src.Socket[1]); break; default: Trace (DEBUG_FAILURES, "Packet with operation %d" " from %.2x%.2x%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x.%.2x%.2x ignored.", packet->Operation, packet->Src.Network[0], packet->Src.Network[1], packet->Src.Network[2], packet->Src.Network[3], packet->Src.Node[0], packet->Src.Node[1], packet->Src.Node[2], packet->Src.Node[3], packet->Src.Node[4], packet->Src.Node[5], packet->Src.Socket[0], packet->Src.Socket[1]); break; } } else Trace (DEBUG_FAILURES, "File: %s, line %ld. Invalid packet.", __FILE__, __LINE__); } // else Receive failure - reported by io layer } // else Loopback packet ReleaseInterfaceReference (intf); } // else Unknown interface - reported by io layer if (InterlockedIncrement (&WorkerQueue.WQ_RequestExtra)<=0) { Trace (DEBUG_REQ, "Requeing receive request item %08lx.", reqItem); reqItem->iow.io.cbBuffer = sizeof (reqItem->packet); EnqueueRecvRequest (&reqItem->iow.io); return; } else InterlockedDecrement (&WorkerQueue.WQ_RequestExtra); } // else Packet received with error or OperationalState is not UP Trace (DEBUG_REQ, "Freeing receive request item %08lx.", reqItem); InterlockedIncrement (&WorkerQueue.WQ_RequestQuota); DeallocateWorker (reqItem); } VOID APIENTRY SendResponse ( PRESP_ITEM respItem ) { GR_FILTER_PARAMS params; if (respItem->iow.io.status==NO_ERROR) { Trace (DEBUG_RESP, "Filling response item %08lx on interface: %d, for type: %04x.", respItem, respItem->intf->index, respItem->svrType); params.svrIdx = 0; params.intf = respItem->intf; params.packet = &respItem->packet; params.localHopCount = IPX_MAX_HOP_COUNT-1; params.localSvr = FALSE; EnumerateServers (respItem->hEnum, respItem->bcast ? SplitHorizonHopCountFilter // Bcast - use split horizon : HopCountFilter, // Send all best servers (except duplicate entries // on looped networks) (LPVOID)¶ms); respItem->iow.io.cbBuffer = FIELD_OFFSET (SAP_BUFFER, Entries[params.svrIdx]); PUTUSHORT (respItem->iow.io.cbBuffer, &respItem->packet.Length); respItem->iow.io.adpt = respItem->intf->adapter.AdapterIndex; if ((params.svrIdx!=0) && (respItem->intf->stats.SapIfOperState==OPER_STATE_UP) && (OperationalState==OPER_STATE_UP)) { EnqueueSendRequest (&respItem->iow.io); return; } } Trace (DEBUG_RESP, "Freeing general response item %08lx for interface: %d.", respItem, respItem->intf->index); ReleaseInterfaceReference (respItem->intf); DeleteListEnumerator (respItem->hEnum); DeallocateWorker (respItem); } /*++ ******************************************************************* I n i t R e s p I t e m Routine Description: Allocate and initialize SAP response item Calls ProcessRespIOItem to fill the packet and send it Arguments: intf - pointer to interface control block to send on svrType - type of servers to put in response packet dst - where to send the response packet bcast - are we responding to broadcasted request Return Value: NO_ERROR - item was initialized and enqueued OK other - operation failed (windows error code) ******************************************************************* --*/ DWORD InitRespItem ( PINTERFACE_DATA intf, USHORT svrType, PIPX_ADDRESS_BLOCK dst, BOOL bcast ) { PRESP_ITEM respItem; DWORD status; if (!AllocateWorker (respItem, RESP_ITEM)) { Trace (DEBUG_FAILURES, "File: %s, line %ld. Could not allocate response item (gle:%ld).", __FILE__, __LINE__, GetLastError ()); return ERROR_NOT_ENOUGH_MEMORY; } AcquireInterfaceReference (intf); // Make sure interface block is locked respItem->hEnum = CreateListEnumerator ( (svrType!=0xFFFF) ? SDB_TYPE_LIST_LINK // Just servers of one type : SDB_HASH_TABLE_LINK, // All servers svrType, NULL, (!Routing && bcast) // Respond with only local // servers if not routing and // request was a broadcast ? INTERNAL_INTERFACE_INDEX : INVALID_INTERFACE_INDEX, 0xFFFFFFFF, 0); // All entries, so we can // detect duplicate servers on looped networks if (respItem->hEnum==NULL) { status = GetLastError (); ReleaseInterfaceReference (intf); DeallocateWorker (respItem); return status; } respItem->iow.worker = ProcessRespIOItem; respItem->iow.io.buffer = (PUCHAR)&respItem->packet; respItem->iow.io.status = NO_ERROR; respItem->tmw.worker = ProcessRespTMItem; respItem->tmw.tm.ExpirationCheckProc = NULL; respItem->intf = intf; SetupIpxSapPacket(&respItem->packet, SAP_GENERAL_RESP, dst->Network, dst->Node, dst->Socket); respItem->svrType = svrType; respItem->bcast = (UCHAR)bcast; Trace (DEBUG_RESP, "Generated general response item %08lx for interface %d.", respItem, respItem->intf->index); if (DelayResponseToGeneral>0) { Trace (DEBUG_RESP, "Delaying general response item %08lx for interface: %d.", respItem, respItem->intf->index); respItem->tmw.tm.dueTime = GetTickCount ()+DelayResponseToGeneral; AddHRTimerRequest (&respItem->tmw.tm); } else SendResponse (respItem); return NO_ERROR; } /*++ ******************************************************************* P r o c e s s R e s p I O I t e m Routine Description: Generate and send response packet Arguments: worker - pointer to work item to process Return Value: None ******************************************************************* --*/ VOID APIENTRY ProcessRespIOItem ( PVOID worker ) { PRESP_ITEM respItem = CONTAINING_RECORD (worker, RESP_ITEM, iow.worker); Trace (DEBUG_RESP, "Processing general response tm item %08lx on interface: %d.", respItem, respItem->intf->index); if (respItem->iow.io.status==NO_ERROR) InterlockedIncrement (&respItem->intf->stats.SapIfOutputPackets); if ( (respItem->intf->stats.SapIfOperState==OPER_STATE_UP) && (OperationalState==OPER_STATE_UP)) { if (GetTickCount()-respItem->iow.io.compTimetmw.tm.dueTime = respItem->iow.io.compTime + IPX_SAP_INTERPACKET_GAP; AddHRTimerRequest (&respItem->tmw.tm); } else SendResponse (respItem); } else { Trace (DEBUG_RESP, "Freeing general response item %08lx for interface: %d.", respItem, respItem->intf->index); ReleaseInterfaceReference (respItem->intf); DeleteListEnumerator (respItem->hEnum); DeallocateWorker (respItem); } } VOID APIENTRY ProcessRespTMItem ( PVOID worker ) { PRESP_ITEM respItem = CONTAINING_RECORD (worker, RESP_ITEM, tmw.worker); Trace (DEBUG_RESP, "Processing general response tm item %08lx on interface: %d.", respItem, respItem->intf->index); if ( (respItem->intf->stats.SapIfOperState==OPER_STATE_UP) && (OperationalState==OPER_STATE_UP)) { SendResponse (respItem); } else { Trace (DEBUG_RESP, "Freeing general response item %08lx for interface: %d.", respItem, respItem->intf->index); ReleaseInterfaceReference (respItem->intf); DeleteListEnumerator (respItem->hEnum); DeallocateWorker (respItem); } } /*++ ******************************************************************* D e l e t e B c a s t I t e m Routine Description: Disposes of resources associated with broadcast work item Arguments: bcastItem - pointer to broadcast work item Return Value: None ******************************************************************* --*/ VOID DeleteBcastItem ( PBCAST_ITEM bcastItem ) { Trace (DEBUG_BCAST, "Freeing broadcast item %08lx for interface: %d.", bcastItem, bcastItem->intf->index); if (bcastItem->chngEnum!=NULL) DeleteListEnumerator (bcastItem->chngEnum); if ((bcastItem->perdEnum!=NULL) && (bcastItem->perdEnum!=INVALID_HANDLE_VALUE)) DeleteListEnumerator (bcastItem->perdEnum); ReleaseInterfaceReference (bcastItem->intf); DeallocateWorker (bcastItem); } /*++ ******************************************************************* D o B r o a d c a s t Routine Description: Check for and broadcast changed servers Check if it is time to do periodic broadcast and start it if so Arguments: bcastItem - pointer to broadcast work item Return Value: None ******************************************************************* --*/ VOID DoBroadcast ( PBCAST_ITEM bcastItem ) { GR_FILTER_PARAMS params; BOOLEAN periodic; params.svrIdx = bcastItem->delayedSvrCount; params.intf = bcastItem->intf; params.packet = &bcastItem->packet; if ( ((bcastItem->intf->stats.SapIfOperState==OPER_STATE_UP) && ((bcastItem->perdEnum!=NULL) // we are already in the middle of // broadcast || IsLater(GetTickCount (),bcastItem->nextBcast))) // or it is // time to start a new one || ((bcastItem->intf->stats.SapIfOperState==OPER_STATE_STOPPING) // or interface is being stopped, so // we need to broadcast the whole // table as deleted && (bcastItem->perdEnum!=INVALID_HANDLE_VALUE)) // This value in the periodic // enumeration handle field means // that we are already done // with this broadcast ) { Trace (DEBUG_BCAST, "Checking for deleted servers on interface: %d.", params.intf->index); EnumerateServers (bcastItem->chngEnum, SplitHorizonDeletedFilter, ¶ms); if (bcastItem->perdEnum==NULL) { // Need to start new boradcast Trace (DEBUG_BCAST, "Starting broadcast enumeration on interface: %d (@ %ld).", bcastItem->intf->index, bcastItem->nextBcast); if (Routing) // Router installation: broadcast all servers bcastItem->perdEnum = CreateListEnumerator ( SDB_HASH_TABLE_LINK, 0xFFFF, NULL, INVALID_INTERFACE_INDEX, 0xFFFFFFFF, 0); else // Standalone SAP agent: only internal servers bcastItem->perdEnum = CreateListEnumerator ( SDB_INTF_LIST_LINK, 0xFFFF, NULL, INTERNAL_INTERFACE_INDEX, 0xFFFFFFFF, 0); // Set the time for next broadcast bcastItem->nextBcast += bcastItem->intf->info.PeriodicUpdateInterval*1000; } if ((params.svrIdxperdEnum!=NULL) && (bcastItem->perdEnum!=INVALID_HANDLE_VALUE)) { Trace (DEBUG_BCAST, "Adding broadcast servers on interface: %d.", params.intf->index); params.localHopCount = IPX_MAX_HOP_COUNT-1; params.localSvr = FALSE; if (!EnumerateServers (bcastItem->perdEnum, SplitHorizonHopCountFilter, ¶ms)) { // All broadcast servers sent, dispose enumeration handle DeleteListEnumerator (bcastItem->perdEnum); Trace (DEBUG_BCAST, "Broadcast enumeration finished on interface:" " %d (@ %ld, next @ %ld).", bcastItem->intf->index, GetTickCount (), bcastItem->nextBcast); if (bcastItem->intf->stats.SapIfOperState==OPER_STATE_UP) bcastItem->perdEnum = NULL; else // Nore that broadcast of the whole table is done bcastItem->perdEnum = INVALID_HANDLE_VALUE; } } if (bcastItem->intf->stats.SapIfOperState==OPER_STATE_STOPPING) { INT i; for (i=0; ipacket.Entries[i].HopCount); } } periodic = TRUE; } else periodic = FALSE; if ((params.svrIdxintf->stats.SapIfOperState==OPER_STATE_UP)) { if (bcastItem->delayedSvrCount==0) bcastItem->delayedSendTime = GetTickCount ()+DelayChangeBroadcast*1000; Trace (DEBUG_BCAST, "Checking for changed servers on interface: %d.", params.intf->index); params.localHopCount = IPX_MAX_HOP_COUNT+1; EnumerateServers (bcastItem->chngEnum, SplitHorizonFilter, ¶ms); } if ((params.svrIdx>0) && (periodic || (params.svrIdx==IPX_SAP_MAX_ENTRY) || IsLater (GetTickCount (), bcastItem->delayedSendTime))) { bcastItem->iow.io.cbBuffer = FIELD_OFFSET (SAP_BUFFER, Entries[params.svrIdx]); PUTUSHORT (bcastItem->iow.io.cbBuffer, &bcastItem->packet.Length); bcastItem->iow.io.adpt = bcastItem->intf->adapter.AdapterIndex; bcastItem->delayedSvrCount = 0; Trace (DEBUG_BCAST, "Broadcasting %d servers on interface: %d.", params.svrIdx, params.intf->index); EnqueueSendRequest (&bcastItem->iow.io); } else if (bcastItem->intf->stats.SapIfOperState==OPER_STATE_UP) { // Nothing to send, go wait in the timer queue bcastItem->delayedSvrCount = params.svrIdx; if (bcastItem->delayedSvrCount>0) { Trace (DEBUG_BCAST, "Delaying change broadcast on interface: %d (%d servers in the packet).", params.intf->index, bcastItem->delayedSvrCount); bcastItem->tmw.tm.dueTime = bcastItem->delayedSendTime; } else { bcastItem->tmw.tm.dueTime = bcastItem->nextBcast; Trace (DEBUG_BCAST, "Nothing to send, waiting for next broadcast time on interface: %d.", params.intf->index); } AddLRTimerRequest (&bcastItem->tmw.tm); } else // Interface is down or stopping and there are no more stuff to // broadcast, -> go away DeleteBcastItem (bcastItem); } BOOL CheckBcastInterface ( PTM_PARAM_BLOCK tm, PVOID context ) { PBCAST_ITEM bcastItem = CONTAINING_RECORD (tm, BCAST_ITEM, tmw.tm); if (bcastItem->intf->stats.SapIfOperState!=OPER_STATE_UP) return TRUE; else if (bcastItem->intf->index!=PtrToUlong(context)) return TRUE; else return FALSE; } /*++ ******************************************************************* I n i t B c a s t I t e m Routine Description: Allocate and initialize broadcast item Arguments: intf - pointer to interface control block to send on Return Value: NO_ERROR - item was initialized and enqueued OK other - operation failed (windows error code) ******************************************************************* --*/ DWORD InitBcastItem ( PINTERFACE_DATA intf ) { PBCAST_ITEM bcastItem; DWORD status; if (!AllocateWorker (bcastItem, BCAST_ITEM)) { Trace (DEBUG_FAILURES, "File: %s, line %ld. Could not allocate broadcast item (gle:%ld).", __FILE__, __LINE__, GetLastError ()); return ERROR_NOT_ENOUGH_MEMORY; } bcastItem->chngEnum = CreateListEnumerator ( SDB_CHANGE_QUEUE_LINK, 0xFFFF, NULL, Routing ? INVALID_INTERFACE_INDEX : INTERNAL_INTERFACE_INDEX, 0xFFFFFFFF, SDB_DISABLED_NODE_FLAG); if (bcastItem->chngEnum==NULL) { status = GetLastError (); DeallocateWorker (bcastItem); return status; } AcquireInterfaceReference (intf); bcastItem->intf = intf; bcastItem->iow.worker = ProcessBcastIOItem; bcastItem->iow.io.buffer = (PUCHAR)&bcastItem->packet; bcastItem->tmw.worker = ProcessBcastTMItem; bcastItem->tmw.tm.ExpirationCheckProc = CheckBcastInterface; bcastItem->perdEnum = NULL; bcastItem->delayedSvrCount = 0; SetupIpxSapPacket(&bcastItem->packet, SAP_GENERAL_RESP, bcastItem->intf->adapter.Network, IPX_BCAST_NODE, IPX_SAP_SOCKET); bcastItem->nextBcast = GetTickCount (); Trace (DEBUG_BCAST, "Generated broadcast item %08lx for interface %d.", bcastItem, bcastItem->intf->index); DoBroadcast (bcastItem); return NO_ERROR; } /*++ ******************************************************************* P r o c e s s B c a s t I O I t e m Routine Description: Processes broadcast work item that just completed send Arguments: worker - pointer to work item to process Return Value: None ******************************************************************* --*/ VOID APIENTRY ProcessBcastIOItem ( PVOID worker ) { PBCAST_ITEM bcastItem = CONTAINING_RECORD (worker, BCAST_ITEM, iow.worker); ULONG curTime = GetTickCount (); Trace (DEBUG_BCAST, "Processing broadcast io item for interface: %d.", bcastItem->intf->index); // Make sure interface is still up if (bcastItem->iow.io.status==NO_ERROR) { InterlockedIncrement (&bcastItem->intf->stats.SapIfOutputPackets); // Make sure we do not send periodic broadcast packets to fast if ((curTime-bcastItem->iow.io.compTimeperdEnum!=NULL)) { bcastItem->tmw.tm.dueTime = bcastItem->iow.io.compTime + IPX_SAP_INTERPACKET_GAP; AddHRTimerRequest (&bcastItem->tmw.tm); } else DoBroadcast (bcastItem); } else if (bcastItem->intf->stats.SapIfOperState==OPER_STATE_UP) { // Last sent io failed, we better wait before sending next one bcastItem->tmw.tm.dueTime = curTime+SAP_ERROR_COOL_OFF_TIME; AddLRTimerRequest (&bcastItem->tmw.tm); } else // Interface is stopping or down on error, go away DeleteBcastItem (bcastItem); } /*++ ******************************************************************* P r o c e s s B c a s t T M I t e m Routine Description: Processes broadcast work item that just completed wait in timer queue Arguments: worker - pointer to work item to process Return Value: None ******************************************************************* --*/ VOID APIENTRY ProcessBcastTMItem ( PVOID worker ) { PBCAST_ITEM bcastItem = CONTAINING_RECORD (worker, BCAST_ITEM, tmw.worker); Trace (DEBUG_BCAST, "Processing broadcast tm item for interface: %d.", bcastItem->intf->index); if ((bcastItem->intf->stats.SapIfOperState==OPER_STATE_UP) || (bcastItem->intf->stats.SapIfOperState==OPER_STATE_STOPPING)) DoBroadcast (bcastItem); else // Interface is down, go away DeleteBcastItem (bcastItem); } /*++ ******************************************************************* I n i t S r e q I t e m Routine Description: Allocate and initialize send request item (send SAP request on interface) Arguments: intf - pointer to interface control block to send on Return Value: NO_ERROR - item was initialized and enqueued OK other - operation failed (windows error code) ******************************************************************* --*/ DWORD InitSreqItem ( PINTERFACE_DATA intf ) { PSREQ_ITEM sreqItem; if (!AllocateWorker (sreqItem, SREQ_ITEM)) { Trace (DEBUG_FAILURES, "File: %s, line %ld. Could not allocate send request item (gle:%ld.", __FILE__, __LINE__, GetLastError ()); return ERROR_NOT_ENOUGH_MEMORY; } AcquireInterfaceReference (intf); sreqItem->intf = intf; sreqItem->iow.worker = ProcessSreqItem; sreqItem->iow.io.buffer = (PUCHAR)&sreqItem->packet; SetupIpxSapPacket(&sreqItem->packet, SAP_GENERAL_REQ, sreqItem->intf->adapter.Network, IPX_BCAST_NODE, IPX_SAP_SOCKET); sreqItem->packet.Entries[0].Type = 0xFFFF; sreqItem->iow.io.cbBuffer = FIELD_OFFSET (SAP_BUFFER, Entries[0].Type) +sizeof (sreqItem->packet.Entries[0].Type); PUTUSHORT (sreqItem->iow.io.cbBuffer, &sreqItem->packet.Length); sreqItem->iow.io.adpt = sreqItem->intf->adapter.AdapterIndex; Trace (DEBUG_SREQ, "Generated general request item: %08lx on interface: %d.", sreqItem, sreqItem->intf->index); if ((sreqItem->intf->stats.SapIfOperState==OPER_STATE_UP) && (OperationalState==OPER_STATE_UP)) { EnqueueSendRequest (&sreqItem->iow.io); return NO_ERROR; } else { // Interface got changed or deleted Trace (DEBUG_SREQ, "Freing general request item: %08lx for changed or deleted interface %ld.", sreqItem, sreqItem->intf->index); ReleaseInterfaceReference (sreqItem->intf); DeallocateWorker (sreqItem); return ERROR_INVALID_HANDLE; } } /*++ ******************************************************************* P r o c e s s S r e q I t e m Routine Description: Processes send request work item that just completed io Arguments: worker - pointer to work item to process Return Value: None ******************************************************************* --*/ VOID APIENTRY ProcessSreqItem ( PVOID worker ) { PSREQ_ITEM sreqItem = CONTAINING_RECORD (worker, SREQ_ITEM, iow.worker); if (sreqItem->iow.io.status==NO_ERROR) InterlockedIncrement (&sreqItem->intf->stats.SapIfOutputPackets); Trace (DEBUG_SREQ, "Freeing general request item %08lx.", sreqItem); // Just release all resources ReleaseInterfaceReference (sreqItem->intf); DeallocateWorker (sreqItem); } /*++ ******************************************************************* I n i t L P C I t e m Routine Description: Allocate and initialize LPC work item Arguments: None Return Value: NO_ERROR - item was initialized and enqueued OK other - operation failed (windows error code) ******************************************************************* --*/ DWORD InitLPCItem ( void ) { PLPC_ITEM lpcItem; if (!AllocateWorker (lpcItem, LPC_ITEM)) { Trace (DEBUG_FAILURES, "File: %s, line %ld. Could not allocate lpc item (gle:%ld.", __FILE__, __LINE__, GetLastError ()); return ERROR_NOT_ENOUGH_MEMORY; } lpcItem->lpcw.lpc.request = &lpcItem->request; lpcItem->lpcw.worker = ProcessLPCItem; Trace (DEBUG_LPCREQ, "Generated lpc request item %08lx.", lpcItem); // Posts request and awaits completion return ProcessLPCRequests (&lpcItem->lpcw.lpc); } /*++ ******************************************************************* P r o c e s s L P C I t e m Routine Description: Processes LPC request and sends reply Arguments: worker - pointer to work item to process Return Value: None ******************************************************************* --*/ VOID APIENTRY ProcessLPCItem ( PVOID worker ) { PLPC_ITEM lpcItem = CONTAINING_RECORD (worker, LPC_ITEM, lpcw.worker); IPX_SERVER_ENTRY_P server; NWSAP_REPLY_MESSAGE reply; DWORD status; BOOL newServer; Trace (DEBUG_LPCREQ, "Processing lpc request for client: %08lx.", lpcItem->lpcw.lpc.client); if (lpcItem->lpcw.lpc.client==NULL) { Trace (DEBUG_LPCREQ, "Freeing lpc item %08lx.", lpcItem); DeallocateWorker (lpcItem); return; } switch (lpcItem->request.MessageType) { case NWSAP_LPCMSG_ADDADVERTISE: server.Type = lpcItem->request.Message.AdvApi.ServerType; IpxNameCpy (server.Name, lpcItem->request.Message.AdvApi.ServerName); IpxAddrCpy (&server, (PIPX_ADDRESS_BLOCK)lpcItem->request.Message.AdvApi.ServerAddr); // If net or node number are not set, use internal network // parameters that we obtained from the adapter if ((IpxNetCmp (server.Network, IPX_INVALID_NET)==0) || (IpxNodeCmp (server.Node, IPX_INVALID_NODE)==0)) { IpxNetCpy (server.Network, INTERNAL_IF_NET); IpxNodeCpy (server.Node, INTERNAL_IF_NODE); } server.HopCount = 0; status = UpdateServer (&server, INTERNAL_INTERFACE_INDEX, IPX_PROTOCOL_LOCAL, INFINITE, IPX_BCAST_NODE, lpcItem->request.Message.AdvApi.RespondNearest ? 0 : SDB_DONT_RESPOND_NODE_FLAG, &newServer); switch (status) { case NO_ERROR: if (newServer) reply.Error = SAPRETURN_SUCCESS; else reply.Error = SAPRETURN_EXISTS; IpxAddrCpy ((PIPX_ADDRESS_BLOCK)reply.Message.AdvApi.ServerAddr, &server); Trace (DEBUG_LPCREQ, "\t%s server: type %04x, name %.48s.", newServer ? "added" : "updated", server.Type, server.Name); break; case ERROR_NOT_ENOUGH_MEMORY: default: reply.Error = SAPRETURN_NOMEMORY; break; } break; case NWSAP_LPCMSG_REMOVEADVERTISE: server.Type = lpcItem->request.Message.AdvApi.ServerType; IpxNameCpy (server.Name, lpcItem->request.Message.AdvApi.ServerName); IpxAddrCpy (&server, (PIPX_ADDRESS_BLOCK)lpcItem->request.Message.AdvApi.ServerAddr); // If net or node number are not set, use internal network // parameters that we obtained from the adapter if ((IpxNetCmp (server.Network, IPX_INVALID_NET)==0) || (IpxNodeCmp (server.Node, IPX_INVALID_NODE)==0)) { IpxNetCpy (server.Network, INTERNAL_IF_NET); IpxNodeCpy (server.Node, INTERNAL_IF_NODE); } server.HopCount = IPX_MAX_HOP_COUNT; Trace (DEBUG_LPCREQ, "About to call UpdateServer because of NWSAP_LPCMSG_REMOVEADVERTISE"); status = UpdateServer (&server, INTERNAL_INTERFACE_INDEX, IPX_PROTOCOL_LOCAL, INFINITE, IPX_BCAST_NODE, 0, &newServer); switch (status) { case NO_ERROR: if (newServer) reply.Error = SAPRETURN_NOTEXIST; else reply.Error = SAPRETURN_SUCCESS; Trace (DEBUG_LPCREQ, "\t%s server: type %04x, name %.48s.", newServer ? "already gone" : "deleted", server.Type, server.Name); break; case ERROR_NOT_ENOUGH_MEMORY: default: reply.Error = SAPRETURN_NOMEMORY; break; } break; case NWSAP_LPCMSG_GETOBJECTID: if (QueryServer( lpcItem->request.Message.BindLibApi.ObjectType, lpcItem->request.Message.BindLibApi.ObjectName, NULL, NULL, NULL, &reply.Message.BindLibApi.ObjectID)) { // Trace (DEBUG_ENTRIES, "\tgot id %0lx for server: type %04x, name %.48s.", // reply.Message.BindLibApi.ObjectID, // lpcItem->request.Message.BindLibApi.ObjectType, // lpcItem->request.Message.BindLibApi.ObjectName); reply.Message.BindLibApi.ObjectID |= BINDLIB_NCP_SAP; reply.Error = SAPRETURN_SUCCESS; } else { Trace (DEBUG_LPCREQ, "\tno server: type %04x, name %.48s.", lpcItem->request.Message.BindLibApi.ObjectType, lpcItem->request.Message.BindLibApi.ObjectName); switch (GetLastError ()) { case NO_ERROR: reply.Error = SAPRETURN_NOTEXIST; break; case ERROR_NOT_ENOUGH_MEMORY: default: reply.Error = SAPRETURN_NOMEMORY; break; } } break; case NWSAP_LPCMSG_GETOBJECTNAME: if (((lpcItem->request.Message.BindLibApi.ObjectID & BINDLIB_NCP_SAP)==BINDLIB_NCP_SAP) && (lpcItem->request.Message.BindLibApi.ObjectID request.Message.BindLibApi.ObjectID & SDB_OBJECT_ID_MASK; if (GetServerFromID( reply.Message.BindLibApi.ObjectID, &server, NULL, NULL)) { reply.Error = SAPRETURN_SUCCESS; reply.Message.BindLibApi.ObjectID |= BINDLIB_NCP_SAP; reply.Message.BindLibApi.ObjectType = server.Type; IpxNameCpy (reply.Message.BindLibApi.ObjectName, server.Name); IpxAddrCpy ((PIPX_ADDRESS_BLOCK)reply.Message.BindLibApi.ObjectAddr, &server); // Trace (DEBUG_ENTRIES, // "\tgot server: type %04x, name %.48s from id %0lx.", // reply.Message.BindLibApi.ObjectType, // reply.Message.BindLibApi.ObjectName, // lpcItem->request.Message.BindLibApi.ObjectID); } else { switch (GetLastError ()) { case NO_ERROR: Trace (DEBUG_LPCREQ, "\tno server for id %0lx.", lpcItem->request.Message.BindLibApi.ObjectID); reply.Error = SAPRETURN_NOTEXIST; break; case ERROR_NOT_ENOUGH_MEMORY: default: reply.Error = SAPRETURN_NOMEMORY; break; } } } else { Trace (DEBUG_LPCREQ, "\tInvalid object id in get name request %0lx.", lpcItem->request.Message.BindLibApi.ObjectID); reply.Error = SAPRETURN_NOTEXIST; } break; case NWSAP_LPCMSG_SEARCH: if ((lpcItem->request.Message.BindLibApi.ObjectID == SDB_INVALID_OBJECT_ID) || (((lpcItem->request.Message.BindLibApi.ObjectID & BINDLIB_NCP_SAP)==BINDLIB_NCP_SAP) && (lpcItem->request.Message.BindLibApi.ObjectID request.Message.BindLibApi.ObjectID == SDB_INVALID_OBJECT_ID) reply.Message.BindLibApi.ObjectID = SDB_INVALID_OBJECT_ID; else reply.Message.BindLibApi.ObjectID = lpcItem->request.Message.BindLibApi.ObjectID & SDB_OBJECT_ID_MASK; if (GetNextServerFromID ( &reply.Message.BindLibApi.ObjectID, lpcItem->request.Message.BindLibApi.ScanType, &server, NULL, NULL)) { reply.Message.BindLibApi.ObjectID |= BINDLIB_NCP_SAP; reply.Error = SAPRETURN_SUCCESS; reply.Message.BindLibApi.ObjectType = server.Type; IpxNameCpy (reply.Message.BindLibApi.ObjectName, server.Name); IpxAddrCpy ((PIPX_ADDRESS_BLOCK)reply.Message.BindLibApi.ObjectAddr, &server); // Trace (DEBUG_ENTRIES, // "\tgot next server: type %04x, name %.48s, id %0lx from id %0lx.", // reply.Message.BindLibApi.ObjectType, // reply.Message.BindLibApi.ObjectName, // reply.Message.BindLibApi.ObjectID, // lpcItem->request.Message.BindLibApi.ObjectID); } else { switch (GetLastError ()) { case NO_ERROR: Trace (DEBUG_LPCREQ, "\tno next server for id %0lx.", lpcItem->request.Message.BindLibApi.ObjectID); reply.Error = SAPRETURN_NOTEXIST; break; case ERROR_NOT_ENOUGH_MEMORY: default: reply.Error = SAPRETURN_NOMEMORY; break; } } } else { Trace (DEBUG_LPCREQ, "\tInvalid object id in get next request %0lx.", lpcItem->request.Message.BindLibApi.ObjectID); reply.Error = SAPRETURN_NOTEXIST; } break; default: Trace (DEBUG_FAILURES, "Got unknown LPC SAP msg: %d.", lpcItem->request.MessageType ); reply.Error = 1; break; } SendLPCReply (lpcItem->lpcw.lpc.client, &lpcItem->request, &reply); Trace (DEBUG_LPCREQ, "Freeing lpc item %08lx.", lpcItem); DeallocateWorker (lpcItem); } /*++ ******************************************************************* I n i t G n e a r I t e m Routine Description: Allocate and initialize GETNEAREST response work item Arguments: intf - pointer to interface control block to send on svrType - type of servers to put in response packet dst - where to send the response packet Return Value: NO_ERROR - item was initialized and enqueued OK other - operation failed (windows error code) ******************************************************************* --*/ DWORD InitGnearItem ( PINTERFACE_DATA intf, USHORT svrType, PIPX_ADDRESS_BLOCK dst ) { DWORD status; GN_FILTER_PARAMS params; HANDLE hEnum; PGNEAR_ITEM gnearItem; if (!AllocateWorker (gnearItem, GNEAR_ITEM)) { Trace (DEBUG_FAILURES, "File: %s, line %ld. Could not allocate get nearest response item (gle:%ld.", __FILE__, __LINE__, GetLastError ()); return ERROR_NOT_ENOUGH_MEMORY; } AcquireInterfaceReference (intf); gnearItem->intf = intf; Trace (DEBUG_GET_NEAREST, "Generated get nearest response item %08lx for server of type: %04x on interface: %ld.", gnearItem, svrType, intf->index); hEnum = CreateListEnumerator ( SDB_TYPE_LIST_LINK, svrType, NULL, Routing ? INVALID_INTERFACE_INDEX : INTERNAL_INTERFACE_INDEX, 0xFFFFFFFF, SDB_MAIN_NODE_FLAG); if (hEnum==NULL) { status = GetLastError (); ReleaseInterfaceReference (intf); DeallocateWorker (gnearItem); return status; } params.found = FALSE; params.intf = gnearItem->intf; params.packet = &gnearItem->packet; params.localHopCount = IPX_MAX_HOP_COUNT-1; EnumerateServers (hEnum, GetNearestFilter, (LPVOID)¶ms); DeleteListEnumerator (hEnum); if (params.found) { gnearItem->iow.worker = ProcessGnearItem; gnearItem->iow.io.buffer = (PUCHAR)&gnearItem->packet; gnearItem->intf = intf; SetupIpxSapPacket(&gnearItem->packet, SAP_GET_NEAREST_RESP, dst->Network, dst->Node, dst->Socket); gnearItem->iow.io.cbBuffer = FIELD_OFFSET (SAP_BUFFER, Entries[1]); PUTUSHORT (gnearItem->iow.io.cbBuffer, &gnearItem->packet.Length); gnearItem->iow.io.adpt = gnearItem->intf->adapter.AdapterIndex; Trace (DEBUG_GET_NEAREST, "Sending get nearest reply (type %04x, name:%.48s, hops:%d) on interface %ld.", GETUSHORT (&gnearItem->packet.Entries[0].Type), gnearItem->packet.Entries[0].Name, GETUSHORT (&gnearItem->packet.Entries[0].HopCount), intf->index); if ((gnearItem->intf->stats.SapIfOperState==OPER_STATE_UP) && (OperationalState==OPER_STATE_UP)) { EnqueueSendRequest (&gnearItem->iow.io); return NO_ERROR; } } Trace (DEBUG_GET_NEAREST, "Freeing get nearest response item %08lx (nothing to reply) for interface %ld.", gnearItem, gnearItem->intf->index); ReleaseInterfaceReference (gnearItem->intf); DeallocateWorker (gnearItem); return NO_ERROR; } /*++ ******************************************************************* P r o c e s s G n e a r I t e m Routine Description: Processes completed GETNEAREST work item Arguments: worker - pointer to work item to process Return Value: None ******************************************************************* --*/ VOID APIENTRY ProcessGnearItem ( PVOID worker ) { PGNEAR_ITEM gnearItem = CONTAINING_RECORD (worker, GNEAR_ITEM, iow.worker); if (gnearItem->iow.io.status==NO_ERROR) InterlockedIncrement (&gnearItem->intf->stats.SapIfOutputPackets); Trace (DEBUG_GET_NEAREST, "Freeing get nearest response item %08lx for interface %ld.", gnearItem, gnearItem->intf->index); ReleaseInterfaceReference (gnearItem->intf); DeallocateWorker (gnearItem); } BOOL CheckInterfaceDown ( PTM_PARAM_BLOCK tm, PVOID context ) { PTREQ_ITEM treqItem = CONTAINING_RECORD (tm, TREQ_ITEM, tmw.tm); return treqItem->intf->stats.SapIfOperState!=OPER_STATE_UP; } /*++ ******************************************************************* I n i t T r e q I t e m Routine Description: Allocate and initialize triggered request item (send SAP request on interface and wait for responces to arrive) Arguments: intf - pointer to interface control block to send on Return Value: NO_ERROR - item was initialized and enqueued OK other - operation failed (windows error code) ******************************************************************* --*/ DWORD InitTreqItem ( PINTERFACE_DATA intf ) { PTREQ_ITEM treqItem; HANDLE enumHdl; enumHdl = CreateListEnumerator ( SDB_INTF_LIST_LINK, 0xFFFF, NULL, intf->index, IPX_PROTOCOL_SAP, SDB_DISABLED_NODE_FLAG); if (enumHdl==NULL) return GetLastError (); EnumerateServers (enumHdl, DeleteAllServersCB, enumHdl); DeleteListEnumerator (enumHdl); if (!AllocateWorker (treqItem, TREQ_ITEM)) { Trace (DEBUG_FAILURES, "File: %s, line %ld. Could not allocate triggered request item (gle:%ld).", __FILE__, __LINE__, GetLastError ()); return ERROR_NOT_ENOUGH_MEMORY; } AcquireInterfaceReference (intf); treqItem->intf = intf; treqItem->iow.worker = ProcessTreqIOItem; treqItem->iow.io.buffer = (PUCHAR)&treqItem->packet; treqItem->tmw.worker = ProcessTreqTMItem; treqItem->tmw.tm.ExpirationCheckProc = CheckInterfaceDown; SetupIpxSapPacket(&treqItem->packet, SAP_GENERAL_REQ, treqItem->intf->adapter.Network, IPX_BCAST_NODE, IPX_SAP_SOCKET); treqItem->packet.Entries[0].Type = 0xFFFF; treqItem->iow.io.cbBuffer = FIELD_OFFSET (SAP_BUFFER, Entries[0].Type) +sizeof (treqItem->packet.Entries[0].Type); PUTUSHORT (treqItem->iow.io.cbBuffer, &treqItem->packet.Length); treqItem->iow.io.adpt = treqItem->intf->adapter.AdapterIndex; treqItem->listenSave = treqItem->intf->info.Listen; treqItem->intf->info.Listen = ADMIN_STATE_ENABLED; treqItem->intervalSave = treqItem->intf->info.PeriodicUpdateInterval; treqItem->intf->info.PeriodicUpdateInterval = MAXULONG; treqItem->resend = 0; treqItem->pktCount = treqItem->intf->stats.SapIfInputPackets; Trace (DEBUG_TREQ, "Generated triggered request item %08lx on interface %d.", treqItem, treqItem->intf->index); if ((treqItem->intf->stats.SapIfOperState==OPER_STATE_UP) && (OperationalState==OPER_STATE_UP)) { EnqueueSendRequest (&treqItem->iow.io); return NO_ERROR; } else { // Interface got changed or deleted Trace (DEBUG_TREQ, "Freeing triggered request item %08lx for changed or deleted interface %ld.", treqItem, treqItem->intf->index); treqItem->intf->info.Listen = treqItem->listenSave; treqItem->intf->info.PeriodicUpdateInterval = treqItem->intervalSave; ReleaseInterfaceReference (treqItem->intf); DeallocateWorker (treqItem); return ERROR_CAN_NOT_COMPLETE; } } /*++ ******************************************************************* R e t u r n U p d a t e R e s u l t Routine Description: Sets up parameter block and enquues results of update to async result queue Arguments: treqItem - pointer to triggered request item that has completed the update status - result of update performted by treqItem Return Value: NO_ERROR - item was initialized and enqueued OK other - operation failed (windows error code) ******************************************************************* --*/ VOID ReturnUpdateResult ( PTREQ_ITEM treqItem, DWORD status ) { Trace (DEBUG_TREQ, "Reporting triggered request result (res:%d, count:%d)" " for interface: %d.", status, treqItem->pktCount, treqItem->intf->index); treqItem->ar.event = UPDATE_COMPLETE; treqItem->ar.message.UpdateCompleteMessage.InterfaceIndex = treqItem->intf->index; treqItem->ar.message.UpdateCompleteMessage.UpdateType = DEMAND_UPDATE_SERVICES; treqItem->ar.message.UpdateCompleteMessage.UpdateStatus = status; treqItem->ar.freeRsltCB = &FreeTreqItem; treqItem->intf->info.Listen = treqItem->listenSave; treqItem->intf->info.PeriodicUpdateInterval = treqItem->intervalSave; ReleaseInterfaceReference (treqItem->intf); EnqueueResult (&treqItem->ar); } /*++ ******************************************************************* P r o c e s s T r e q I O I t e m Routine Description: Processes triggered request work item that just completed io Arguments: worker - pointer to work item to process Return Value: None ******************************************************************* --*/ VOID APIENTRY ProcessTreqIOItem ( PVOID worker ) { PTREQ_ITEM treqItem = CONTAINING_RECORD (worker, TREQ_ITEM, iow.worker); HANDLE hEnum; if (treqItem->iow.io.status==NO_ERROR) InterlockedIncrement (&treqItem->intf->stats.SapIfOutputPackets); Trace (DEBUG_TREQ, "Processing triggered request io item for interface: %d.", treqItem->intf->index); if ((treqItem->intf->stats.SapIfOperState==OPER_STATE_UP) && (OperationalState==OPER_STATE_UP)) { treqItem->resend += 1; treqItem->tmw.tm.dueTime = GetTickCount () + TriggeredUpdateCheckInterval*1000; AddLRTimerRequest (&treqItem->tmw.tm); return; } ReturnUpdateResult (treqItem, ERROR_CAN_NOT_COMPLETE); } /*++ ******************************************************************* P r o c e s s T r e q T M I t e m Routine Description: Processes triggered request work item that just completed timer wait Arguments: worker - pointer to work item to process Return Value: None ******************************************************************* --*/ VOID APIENTRY ProcessTreqTMItem ( PVOID worker ) { PTREQ_ITEM treqItem = CONTAINING_RECORD (worker, TREQ_ITEM, tmw.worker); ULONG count = treqItem->intf->stats.SapIfInputPackets; Trace (DEBUG_TREQ, "Processing triggered request tm item for interface: %d.", treqItem->intf->index); if ((treqItem->intf->stats.SapIfOperState==OPER_STATE_UP) && (OperationalState==OPER_STATE_UP)) { if (treqItem->pktCount!=count) { Trace (DEBUG_TREQ, "\t%d more packets received during last check period.", treqItem->intf->stats.SapIfInputPackets - treqItem->pktCount); treqItem->pktCount = count; treqItem->tmw.tm.dueTime = GetTickCount () + TriggeredUpdateCheckInterval*1000; AddLRTimerRequest (&treqItem->tmw.tm); } else if (treqItem->resendresend+1); treqItem->iow.io.cbBuffer = FIELD_OFFSET (SAP_BUFFER, Entries[0].Type) +sizeof (treqItem->packet.Entries[0].Type); EnqueueSendRequest (&treqItem->iow.io); } else ReturnUpdateResult (treqItem, NO_ERROR); return; } ReturnUpdateResult (treqItem, ERROR_CAN_NOT_COMPLETE); } /*++ ******************************************************************* P r o c e s s T r e q A R I t e m Routine Description: Processes triggered request work item that was reported to client in result queue Arguments: worker - pointer to work item to process Return Value: None ******************************************************************* --*/ VOID FreeTreqItem ( PAR_PARAM_BLOCK rslt ) { PTREQ_ITEM treqItem = CONTAINING_RECORD (rslt, TREQ_ITEM, ar); Trace (DEBUG_TREQ, "Freeing triggered request item %08lx.", treqItem); DeallocateWorker (treqItem); } /*++ ******************************************************************* S a p B u i l d I n t e r n a l U p d a t e L i s t F i l t e r Routine Description: Server enumeration callback proc createas a list of local servers that need to have their internal network numbers updated. Arguments: CBParam - pointer to a list of SERVER_INTERNAL_UPDATE_NODE's Return Value: TRUE (to stop enumeration) FALSE otherwise ******************************************************************* --*/ BOOL SapBuildInternalUpdateListFilter ( IN LPVOID CBParam, IN OUT PIPX_SERVER_ENTRY_P Server, IN ULONG InterfaceIndex, IN ULONG Protocol, IN PUCHAR AdvertisingNode, IN INT Flags) { IPX_SERVER_ENTRY_P TempServer; SERVER_INTERNAL_UPDATE_NODE * pNew, **ppList; // Get the list that we're dealing with ppList = (SERVER_INTERNAL_UPDATE_NODE**)CBParam; // If this is a local server with an out of date network number // stored, then add it to the list of servers to update. if (InterfaceIndex == INTERNAL_INTERFACE_INDEX) { if (IpxNetCmp (Server->Network, INTERNAL_IF_NET) != 0) { // Send some trace Trace (DEBUG_SERVERDB, "Updating local server: %s %x%x%x%x:%x%x%x%x%x%x:%x%x", Server->Name, 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] ); // Create and initialize the new node pNew = HeapAlloc (ServerTable.ST_Heap, 0, sizeof (SERVER_INTERNAL_UPDATE_NODE)); if (!pNew) return TRUE; CopyMemory (&(pNew->Server), Server, sizeof (IPX_SERVER_ENTRY_P)); pNew->InterfaceIndex = InterfaceIndex; pNew->Protocol = Protocol; pNew->AdvertisingNode = AdvertisingNode; pNew->Flags = Flags; // Insert the flag in the list if (*ppList) pNew->pNext = *ppList; else pNew->pNext = NULL; *ppList = pNew; } } return FALSE; } // // When the internal network number changes, we need to update the // control blocks of the internal servers. // DWORD SapUpdateLocalServers () { SERVER_INTERNAL_UPDATE_NODE * pList = NULL, * pCur; BOOL bNewServer = FALSE; HANDLE hEnum; Trace (DEBUG_SERVERDB, "SapUpdateLocalServers: entered."); // Create a list enumerator that goes through all // servers in the table. hEnum = CreateListEnumerator ( SDB_HASH_TABLE_LINK, 0xFFFF, NULL, INVALID_INTERFACE_INDEX, 0xFFFFFFFF, 0); if (hEnum == NULL) return GetLastError (); // Enumerate the servers sending them through a filter // that updates their network number and node EnumerateServers (hEnum, SapBuildInternalUpdateListFilter, (LPVOID)&pList); DeleteListEnumerator (hEnum); // pList will now point to a list of servers that need to have // their information updated. while (pList) { pCur = pList; // Send out a broadcast that the local server is now // unreachable pCur->Server.HopCount = IPX_MAX_HOP_COUNT; UpdateServer ( &(pCur->Server), pCur->InterfaceIndex, pCur->Protocol, INFINITE, pCur->AdvertisingNode, pCur->Flags, &bNewServer ); Trace (DEBUG_SERVERDB, "%s has been marked with hop count 16", pCur->Server.Name); // Update the network and node number and advertise that // it is available IpxNetCpy (pCur->Server.Network, INTERNAL_IF_NET); IpxNodeCpy (pCur->Server.Node, INTERNAL_IF_NODE); pCur->Server.HopCount = 0; UpdateServer ( &(pCur->Server), pCur->InterfaceIndex, pCur->Protocol, INFINITE, pCur->AdvertisingNode, pCur->Flags, &bNewServer ); Trace (DEBUG_SERVERDB, "%s has been updated.", pCur->Server.Name); // Cleanup pList = pList->pNext; HeapFree (ServerTable.ST_Heap, 0, pCur); } return NO_ERROR; }