Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1789 lines
44 KiB

/*++
Copyright (c) 1994 Microsoft Corporation
Copyright (c) 1993 Micro Computer Systems, Inc.
Module Name:
net\svcdlls\nwsap\server\nwsap.c
Abstract:
This is the main routine for the NT SAP Agent
Author:
Brian Walker (MCS) 06-15-1993
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
/** Internal Function Prototypes **/
DWORD WINAPI
SapWorkerThread(
LPVOID Threadparm);
DWORD WINAPI
SapRecvThread(
LPVOID Threadparm);
INT
SapHandleRequest(
PUCHAR Requestp,
INT Length,
PUCHAR Addrptr,
INT Addrlength);
INT
SapStartRecvThread(
VOID);
INT
SapGetNearestFromSendList(
PUCHAR Bufferp,
USHORT ServerType);
INT
SapGetCardFromAddress(
PUCHAR Addrptr,
BOOL *WanFlagp,
PUCHAR NetNump);
/*++
*******************************************************************
S a p I n i t
Routine Description:
This routine initializes the Sap Agent.
Arguments:
None
Return Value:
0 = OK
Else = Error
*******************************************************************
--*/
INT
SapInit(
VOID)
{
INT rc;
INT i;
/**
These things need to be initialized before anything
else can happen.
**/
InitializeCriticalSection(&SapRecvCriticalSection);
InitializeCriticalSection(&SapFreeCriticalSection);
InitializeCriticalSection(&SapWanRecvCriticalSection);
InitializeCriticalSection(&SapWanFreeCriticalSection);
InitializeCriticalSection(&SapSendCriticalSection);
InitializeCriticalSection(&SapSendBusyCriticalSection);
InitializeCriticalSection(&SapThreadCountCriticalSection);
InitializeCriticalSection(&SapLpcThreadCountCriticalSection);
InitializeCriticalSection(&SapLpcClientCriticalSection);
InitializeCriticalSection(&SapMemoryCriticalSection);
InitializeCriticalSection(&SapCardlistCriticalSection);
InitializeCriticalSection(&SdmdCriticalSection);
SapCurBackup = 0;
SapThreadCount = 0;
SapLpcNumWorkers = 0;
SapWorkerThreadWaiting = 0;
/** Init the recv buffer list **/
InitializeListHead(&SapRecvList);
InitializeListHead(&SapFreeList);
InitializeListHead(&SapWanRecvList);
InitializeListHead(&SapWanFreeList);
InitializeListHead(&SapLpcClientList);
SapNumFreeBufs = 0;
/** Initialize the Critical Section we use for synch. **/
SapCardlistLockCount = 0;
SapCardlistSynchEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
if (SapCardlistSynchEvent == NULL) {
SapError = GetLastError();
SapEventId = NWSAP_EVENT_CARDLISTEVENT_FAIL;
return -1;
}
/** Go initialize the filter stuff **/
rc = SapFilterInit();
if (rc) {
return rc;
}
/** Go read the registry **/
rc = SapReadRegistry();
if (rc) {
return rc;
}
/** Go initialize the database routines **/
rc = SapInitSdmd();
if (rc) {
return rc;
}
/** Initialize the WAN checking interface **/
rc = SapWanInit();
if (rc)
return rc;
/** Initialize the sockets interface **/
rc = SapNetworkInit();
if (rc)
return rc;
/** Create an event for waiting for thread termination with **/
SapThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (SapThreadEvent == NULL) {
SapError = GetLastError();
SapEventId = NWSAP_EVENT_THREADEVENT_FAIL;
return -1;
}
/** Create the semaphore we use **/
SapRecvSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
if (SapRecvSem == NULL) {
SapError = GetLastError();
SapEventId = NWSAP_EVENT_RECVSEM_FAIL;
return -1;
}
/** Create an event for the send thread **/
SapSendEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (SapSendEvent == NULL) {
SapError = GetLastError();
SapEventId = NWSAP_EVENT_SENDEVENT_FAIL;
return -1;
}
/** Go init the LPC interface **/
if (SapXsInitialize()) {
return -1;
}
/** Start the worker threads **/
for (i = 0 ; i < SapNumWorkerThreads ; i++) {
rc = SapStartWorkerThread();
if (rc) {
SapEventId = 0; /* Already logged */
return -1;
}
/** Save time of last worker startup **/
SapLastWorkerStartTime = GetCurrentTime();
}
/** Start the receive threads **/
for (i = 0 ; i < SapNumRecvThreads ; i++) {
rc = SapStartRecvThread();
if (rc) {
SapEventId = 0; /* Already logged */
return -1;
}
}
/**
Send a general request so all SAPS on the network will
respond and we will get a list of available servers.
**/
SapSendGeneralRequest(0, NULL);
/** Initialization is OK **/
return 0;
}
/*++
*******************************************************************
S a p S h u t d o w n
Routine Description:
When we are terminating, this routine will clean
up everything.
The SsInitialized flag has been set to 0 before we
are called.
Arguments:
None
Return Value:
Nothing
*******************************************************************
--*/
VOID
SapShutdown(
VOID)
{
PSAP_SERVER Servp;
PSAP_SERVER NServp;
PSAP_RECVBLOCK Recvbuf;
PLIST_ENTRY Listp;
INT i;
INT Max;
HANDLE Handle;
/**
The way the code runs is that we get here only after the
send thread has terminated. We need to wait for all receive
and worker threads to terminate. We will close the handles that
these threads wait on and then wait for all the threads to
terminate.
The receive threads wait on a recvfrom on SapSocket.
The worker threads wait on SapRecvSem.
**/
IF_DEBUG(TERMINATION) {
SS_PRINT(("NWSAP: Shutting down\n"));
}
/** Release so all threads can terminate **/
if (SapRecvSem != NULL) {
ReleaseSemaphore(SapRecvSem, SapCurWorkerThreads + 10, NULL);
}
/** If there is a WAN check thread - kill it **/
if (SapWanNotifyHandlesBuf) {
for (i = 0 ; i < SapNumWanNotifyThreads ; i++) {
/** Set the event for this handle **/
if (SapWanEvent) {
SetEvent(SapWanEvent);
}
else {
/** Take this handle out of the list **/
ACQUIRE_THREADCOUNT_LOCK();
Handle = SapWanNotifyHandlesBuf[i];
SapWanNotifyHandlesBuf[i] = NULL;
RELEASE_THREADCOUNT_LOCK();
if (Handle) {
TerminateThread(Handle, 0);
CloseHandle(Handle);
SAP_DEC_THREAD_COUNT("Wan Check Thread Termination", NULL);
}
}
}
}
/** Kill the WAN semaphore **/
if (SapWanSem != NULL) {
ReleaseSemaphore(SapWanSem, 1, NULL);
}
/** Kill the WAN Event **/
if (SapWanEvent != NULL) {
CloseHandle(SapWanEvent);
}
/**
We need to get the recv threads to stop. When we close
the SapSocket the recvform calls will NOT return with
an error. To make the threads stop - we will send a packet
to ourselves. Kind of a HACK - but the only way to do it.
Send a couple more then we need to just to be sure.
**/
if (SapSocket != INVALID_SOCKET) {
/** Send the packets **/
Max = SapCurReceiveThreads + 2;
for (i = 0 ; i < Max ; i++) {
SapSendGeneralRequest(1, NULL);
}
/** Close the socket **/
closesocket(SapSocket);
}
/** If we have a thread handle - wait for all threads to die **/
if (SapThreadEvent) {
IF_DEBUG(TERMINATION) {
SS_PRINT(("NWSAP: Waiting for all threads to terminate\n"));
}
/** Wait for all threads to die **/
WaitForSingleObjectEx(SapThreadEvent, INFINITE, TRUE);
IF_DEBUG(TERMINATION) {
SS_PRINT(("NWSAP: All threads are terminated\n"));
}
}
/** Free the buffer for holding the wan handles **/
if (SapWanNotifyHandlesBuf) {
ACQUIRE_THREADCOUNT_LOCK();
SAP_FREE(SapWanNotifyHandlesBuf, "Notify Table freed");
RELEASE_THREADCOUNT_LOCK();
}
/** Go shutdown the LPC interface **/
SapXsShutdown();
/**
At this point all threads are terminated. We just need to go
thru and free all memory and close all handles.
**/
/** Close handles **/
if (SapSendEvent != NULL)
CloseHandle(SapSendEvent);
if (SapThreadEvent != NULL)
CloseHandle(SapThreadEvent);
if (SapRecvSem != NULL)
CloseHandle(SapRecvSem);
/** Shutdown WAN Checking **/
SapWanShutdown();
/** Free the send list **/
Servp = SapServHead;
while (Servp) {
NServp = Servp->Next;
SAP_FREE(Servp, "SAP Shutdown 1");
Servp = NServp;
}
/** Free any buffers left in the worker queue **/
while (!IsListEmpty(&SapRecvList)) {
Listp = RemoveHeadList(&SapRecvList);
Recvbuf = CONTAINING_RECORD(Listp, SAP_RECVBLOCK, ListEntry);
SAP_FREE(Recvbuf, "SAP SHUTDOWN 2");
}
/** Free any buffers left in the recv buffer free list **/
while (!IsListEmpty(&SapFreeList)) {
Listp = RemoveHeadList(&SapFreeList);
Recvbuf = CONTAINING_RECORD(Listp, SAP_RECVBLOCK, ListEntry);
SAP_FREE(Recvbuf, "SAP SHUTDOWN 3");
}
/** Close the network interface **/
SapNetworkShutdown();
/** Cleanup the database **/
SapShutdownSdmd();
/** Cleanup the filter stuff **/
SapFilterShutdown();
/** Cleanup all critical sections **/
DeleteCriticalSection(&SapWanRecvCriticalSection);
DeleteCriticalSection(&SapWanFreeCriticalSection);
DeleteCriticalSection(&SdmdCriticalSection);
DeleteCriticalSection(&SapRecvCriticalSection);
DeleteCriticalSection(&SapFreeCriticalSection);
DeleteCriticalSection(&SapSendCriticalSection);
DeleteCriticalSection(&SapSendBusyCriticalSection);
DeleteCriticalSection(&SapMemoryCriticalSection);
DeleteCriticalSection(&SapThreadCountCriticalSection);
DeleteCriticalSection(&SapLpcThreadCountCriticalSection);
DeleteCriticalSection(&SapLpcClientCriticalSection);
DeleteCriticalSection(&SapCardlistCriticalSection);
/** All Done **/
return;
}
/*++
*******************************************************************
S a p S e n d T h r e a d
Routine Description:
This thread sends SAP announcements at the interval
specified in the registry (def = 60 seconds)
Arguments:
Threadparm = Parameter from the CreateThread call
Return Value:
Exit Code
*******************************************************************
--*/
DWORD WINAPI
SapSendThread(
LPVOID Threadparm)
{
NTSTATUS Status;
INT MinutesLeft;
DWORD TimeWait;
/**
The first time thru we wait 10 seconds and
then advertise everybody so that we quickly
bring up new networks.
**/
TimeWait = 10 * 1000;
/** This is the send loop **/
SapRecheckCount = SapRecheckAllCardsTime;
MinutesLeft = SapSendMinutes;
while (SsInitialized) {
/** Wait for one minute **/
Status = WaitForSingleObjectEx(SapSendEvent, TimeWait, TRUE);
/** All other waits are one minute **/
TimeWait = 60 * 1000;
/** If stopping - leave **/
if (!SsInitialized) {
IF_DEBUG(TERMINATION) {
SS_PRINT(("NWSAP SEND THREAD: Exitting the thread\n"));
}
break;
}
/** Check for timeouts in the database **/
SdmdTimeoutCheck();
/**
In the case where our network number is zero, we must
recheck it here. When we started, we could have gotten
a network number of zero from IPX if IPX had not yet
determined its network number from the network.
In this case we keep checking every minute to see if
IPX learned its new network number.
We do this before we send the advertise packets to
that we do not have to wait another minute before
these show up on the wire.
**/
if (!memcmp(SapNetNum, SapZeros, SAP_NET_LEN)) {
INT Retcode;
INT AddressLength;
SOCKADDR Address;
/**
Ask IPX what it's current network number is.
If it changes to non-zero - we need to update.
**/
AddressLength = 16;
Retcode = getsockname(SapSocket, &Address, &AddressLength);
/** If the call succeeds - check the data **/
if (Retcode != SOCKET_ERROR) {
/** Set SapNetNum from the net number returned **/
memcpy(SapNetNum, Address.sa_data, SAP_NET_LEN);
/** If not zero - go update the advertise list **/
if (memcmp(SapNetNum, SapZeros, SAP_NET_LEN)) {
PSAP_SERVER Servp;
/** Lock the list so it doesn't change on us **/
ACQUIRE_SENDTABLE_LOCK();
/**
If any entries have a network number of zero
change them to have a network number of
the network number we just learned from
IPX.
**/
Servp = SapServHead;
while (Servp) {
/** **/
if (!memcmp(Servp->Address, SapZeros, SAP_NET_LEN)) {
memcpy(Servp->Address, SapNetNum, SAP_NET_LEN);
}
/** Goto the next entry **/
Servp = Servp->Next;
}
/** Release the list **/
RELEASE_SENDTABLE_LOCK();
/**
Now we need to update the network numbers
of all the cards we have.
**/
SapUpdateCardNetworkNumbers();
}
}
}
/**
If time is up - go send advertises for everything
we need. This handles advertising of routed SAPs
as well.
If there has been a change in the table - send now
**/
if (SapChanged)
MinutesLeft = 0; /* Send Now */
else
MinutesLeft--;
if (!MinutesLeft) {
/** Do the advertising **/
SapSendPackets(0);
/** Reset the number of minutes **/
MinutesLeft = SapSendMinutes;
}
/**
When a WAN card comes up, we send a general request.
We send the request multiple times in case it got
missed.
**/
SapCheckSendGeneralRequest();
/**
If we are suppossed to go check all cards again -
then we check that here.
**/
if (SapRecheckAllCardsTime) {
SapRecheckCount--;
if (SapRecheckCount == 0) {
SapRecheckAllCards();
SapRecheckCount = SapRecheckAllCardsTime;
}
}
}
/** Send out packets that tell networks about servers going down **/
SapSendPackets(2);
/** All Done **/
return 0;
}
/*++
*******************************************************************
S a p W o r k e r T h r e a d
Routine Description:
This thread takes recv'd SAP packets off of the
recv list and processes them.
Arguments:
threadparm = Parameter from the CreateThread call
Return Value:
Exit Code
*******************************************************************
--*/
DWORD WINAPI
SapWorkerThread(
LPVOID Threadparm)
{
NTSTATUS Status;
PSAP_RECVBLOCK Recvbuf;
PLIST_ENTRY Listp;
INT SendLength;
INT rc;
/** **/
while (SsInitialized) {
/** Wait for a request to show up **/
INC_WORKER_THREAD_WAITING_COUNT();
Status = WaitForSingleObjectEx(SapRecvSem, INFINITE, TRUE);
DEC_WORKER_THREAD_WAITING_COUNT();
/** If stopping - just leave **/
if (!SsInitialized) {
IF_DEBUG(TERMINATION) {
SS_PRINT(("NWSAP: Worker Thread: Breaking out for stop\n"));
}
break;
}
/** If error - just ignore it **/
if (!NT_SUCCESS(Status)) {
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: Worker: Wait failed: status = 0x%lx\n", Status));
}
continue;
}
/** Get ownership of the worker list **/
ACQUIRE_RECVTABLE_LOCK();
/**
If we at in a high traffic time, we might need to start
another worker thread. Check this here. We do this here
to get the protection of the lock.
**/
if ((SapCurBackup > SapNewWorkerThreshhold) &&
(SapCurWorkerThreads < SapMaxEverWorkerThreads) &&
(!SapWorkerStarting)) {
LONG Diff;
/**
Make sure that enough time has elapsed. There is
a registry parameter that lets the user set
the min time between worker startups.
The diff line will give us the number of milliseconds
since the last time we tried to start a new worker.
If we have elapsed enough time - then we can start
a new one.
**/
Diff = abs((LONG)GetCurrentTime() - (LONG)SapLastWorkerStartTime);
if (Diff >= SapNewWorkerTimeout) {
/**
Mark that we are in the worker starting code so we
do not come in here again while we are starting up.
We release the lock so that other worker threads can
be working while we are starting a new one.
**/
SapWorkerStarting = 1;
RELEASE_RECVTABLE_LOCK();
/** Go start a new worker thread **/
rc = SapStartWorkerThread();
/**
If OK - set new time that a worker thread
was started at.
**/
if (!rc) {
SapLastWorkerStartTime = GetCurrentTime();
}
/**
Reacquire the recv table lock and go get
a packet to handle.
Mark that we are no longer in the worker thread
starting code.
**/
ACQUIRE_RECVTABLE_LOCK();
SapWorkerStarting = 0;
}
}
/** Get an entry from the list **/
if (!IsListEmpty(&SapRecvList)) {
Listp = RemoveHeadList(&SapRecvList);
Recvbuf = CONTAINING_RECORD(Listp, SAP_RECVBLOCK, ListEntry);
SapCurBackup--;
}
else
Recvbuf = NULL;
/** Release the lock on the list **/
RELEASE_RECVTABLE_LOCK();
/** If no packet - error **/
if (Recvbuf == NULL) {
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: WORKER: Wait came back but no block ready\n"));
}
continue;
}
/** Handle the packet here **/
SendLength = SapHandleRequest(
Recvbuf->Buffer,
Recvbuf->Datalen,
Recvbuf->Address,
Recvbuf->AddressLength);
/** If we need to send a response - do it **/
if (SendLength && (SendLength != -1)) {
/** Send the response back **/
rc = sendto(
SapSocket, /* Socket */
Recvbuf->Buffer, /* Ptr to send buffer */
SendLength, /* Length of data */
0, /* Flags */
(PSOCKADDR)Recvbuf->Address,
Recvbuf->AddressLength);
/** If error - handle it **/
if (rc == -1) {
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: WORKER: Sendto failed: error = %d\n", h_errno));
}
}
}
/**
If there is room in the FREE list - then put
this entry in the free list, else just free
the memory back to heap.
**/
ACQUIRE_FREETABLE_LOCK();
if (SapNumFreeBufs < SapMaxFreeBufs) {
InsertTailList(&SapFreeList, &Recvbuf->ListEntry);
SapNumFreeBufs++;
}
else {
SAP_FREE(Recvbuf, "FREE RECVBUF");
}
RELEASE_FREETABLE_LOCK();
/** If we are to terminate - do it **/
if (SendLength == -1)
break;
}
/** We are terminating - uncount and set event if need to **/
SAP_DEC_THREAD_COUNT("Worker Terminate", &SapCurWorkerThreads);
/** All Done **/
return 0;
}
/*++
*******************************************************************
S a p R e c v T h r e a d
Routine Description:
This is the thread that recv SAPs and places them
on a list to be handled.
Arguments:
Threadparm = Parameter from the CreateThread call
Return Value:
Exit Code
*******************************************************************
--*/
DWORD WINAPI
SapRecvThread(
LPVOID Threadparm)
{
PSAP_RECVBLOCK Recvbuf;
PLIST_ENTRY Listp;
DWORD lastNetError = 0;
HANDLE SapRecvEvent = NULL;
DWORD status;
SapRecvEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
/** Sit in the loop and recv datagrams **/
while (SsInitialized) {
/** Get ownership of the list **/
ACQUIRE_FREETABLE_LOCK();
if (!IsListEmpty(&SapFreeList)) {
Listp = RemoveHeadList(&SapFreeList);
Recvbuf = CONTAINING_RECORD(Listp, SAP_RECVBLOCK, ListEntry);
SapNumFreeBufs--;
} else {
Recvbuf = SAP_MALLOC(SAP_RECVBLOCK_SIZE + SAP_MAX_RECVLEN, "Make RecvBuf");
if (Recvbuf == NULL) {
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: RECV: Error allocating recvbuf\n"));
}
RELEASE_FREETABLE_LOCK();
WaitForSingleObjectEx( SapRecvEvent,
(SapRecvDelayOnMallocFail * 1000),
TRUE);
continue;
}
}
RELEASE_FREETABLE_LOCK();
/** Hang a recv **/
Recvbuf->AddressLength = 16;
Recvbuf->Datalen = recvfrom(
SapSocket,
Recvbuf->Buffer,
SAP_MAX_RECVLEN,
0,
(PSOCKADDR)Recvbuf->Address,
&Recvbuf->AddressLength);
/** If stopping - then just leave**/
if (!SsInitialized) {
SAP_FREE(Recvbuf, "RECV THREAD Terminate 1");
IF_DEBUG(TERMINATION) {
SS_PRINT(("NWSAP: RECV THREAD: Breaking out for stop\n"));
}
break;
}
/** If error - handle it **/
if (Recvbuf->Datalen == -1) {
lastNetError = h_errno;
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: RECV: Error recving sap: h_errno = %d\n",
lastNetError));
}
//
// Special case getting an invalid length for the incoming packet
// by not delaying.
//
if ( lastNetError != WSAEMSGSIZE &&
lastNetError != WSAEFAULT ) {
WaitForSingleObjectEx( SapRecvEvent,
(SapRecvDelayOnNetError * 1000),
TRUE);
}
/** We got an error on the recv **/
SAP_FREE(Recvbuf, "RECV THREAD Terminate 2");
continue;
}
/** Put the entry on the worker list **/
ACQUIRE_RECVTABLE_LOCK();
if (SapCurBackup < SapMaxBackup) {
InsertTailList(&SapRecvList, &Recvbuf->ListEntry);
SapCurBackup++;
} else {
SAP_FREE(Recvbuf, "RECV THREAD Dropped Request");
Recvbuf = NULL ;
RELEASE_RECVTABLE_LOCK();
continue;
}
RELEASE_RECVTABLE_LOCK();
/** Signal the worker thread there is a recv there **/
ReleaseSemaphore(SapRecvSem, 1, NULL);
}
/** We are terminating - uncount and set event if need to **/
SAP_DEC_THREAD_COUNT("Receive Terminate", &SapCurReceiveThreads);
if (SapRecvEvent != NULL) {
CloseHandle(SapRecvEvent);
}
/** All Done **/
return 0;
}
/*++
*******************************************************************
S a p H a n d l e R e q u e s t
Routine Description:
When we recv a sap packet - this function will handle
it and build a response if it needs to.
Arguments:
Requestp = Ptr to the SAP packet recv'd
Length = Length of the SAP packet
Addrptr = Address of who sent the packet
Addrlength = Length of address at addrp
Return Value:
Length of return packet to send back. If this is 0
then there is no packet to send back.
If -1, then we should terminate this worker thread immediately.
*******************************************************************
--*/
INT
SapHandleRequest(
PUCHAR Requestp,
INT Length,
PUCHAR Addrptr,
INT Addrlength)
{
INT ReturnLength = 0;
INT Cardnum;
PSAP_HEADER Servp;
USHORT QueryType;
USHORT ServerType;
UCHAR BcastFlag;
UCHAR NetNum[SAP_NET_LEN];
BOOL WanFlag;
INT NumFound;
INT rc;
/**
Make sure we have a minimum length. The minimum length
is a USHORT of the query type and a USHORT of the
server type.
**/
if (Length < (sizeof(USHORT) + sizeof(USHORT)))
return 0;
/** Get the query type **/
QueryType = ntohs(*(PUSHORT)Requestp);
switch (QueryType) {
/** General Service Query **/
case SAPTYPE_GENERAL_SERVICE_QUERY:
/** Get the server type wanted **/
ServerType = ntohs(((PSAP_REQUEST)Requestp)->ServerType);
/**
Get flag telling us if this request is broadcast of
a unicast request.
**/
BcastFlag = (*(Addrptr + 15) & 1);
/**
Get the card number that this request came in on.
If the network cannot be found - OR - the request
came from the SAP Agent (ME), then we will not respond.
But if the request came from someone else in this machine
or an external person, then we will respond.
**/
Cardnum = SapGetCardFromAddress(Addrptr, &WanFlag, NULL);
if ((Cardnum == CARDRET_UNKNOWN) || (Cardnum == CARDRET_MYSELF))
return 0;
/**
Send all of that type. The return value will be 0 or -1.
If 0, then nothing is to be returned (we sent all the
packets from the routine). If -1, then this
thread should terminate.
**/
ReturnLength = SapSendForTypes(
ServerType,
Addrptr,
Addrlength,
Cardnum,
BcastFlag,
WanFlag);
break;
/** Nearest service query **/
case SAPTYPE_NEAREST_SERVICE_QUERY:
/** Verify we have a good server type **/
ServerType = ntohs(((PSAP_REQUEST)Requestp)->ServerType);
if (ServerType == 0xFFFF) /* Invalid */
break;
/**
Get the card that we received this on. If we get
that we cannot map the card net number, then
just ignore the packet.
retlen == 0 when we get here so we do not need to
set it.
**/
Cardnum = SapGetCardFromAddress(Addrptr, &WanFlag, NULL);
if (Cardnum == CARDRET_UNKNOWN)
break;
/**
Get flag telling us if this request is broadcast of
a unicast request.
**/
BcastFlag = (*(Addrptr + 15) & 1);;
/**
If this is NOT a WAN card, then we will first check
our send list for a server and then if one is not found
there, then we will check the database for another server.
**/
if (!WanFlag) {
/** Find one we are advertising **/
ReturnLength = SapGetNearestFromSendList(Requestp, ServerType);
if (ReturnLength) /* Found one - return it */
break;
/**
We did not find a nearest server in our advertise list,
so go find one in the database.
**/
ReturnLength = SdmdGetNearestServerLan(
Requestp, /* Ptr to request buffer */
ServerType, /* Server type they want */
Cardnum, /* Card req recv'd on */
BcastFlag); /* Req bcast/unicast flag */
/** Return this value (Calling ret will send response) **/
break;
}
/**
This request came over a WAN link. We will find up to
4 servers to send back to the requestee.
**/
ReturnLength = SdmdGetNearestServerWan(
Requestp, /* Ptr to request buffer */
ServerType, /* Server type they want */
Cardnum, /* Card req recv'd on */
BcastFlag, /* Req bcast/unicast flag */
&NumFound); /* Num found */
/**
We found some responses. NumFound is set to the number
we have. We send them all back here.
**/
while (NumFound--) {
/** Send a packet **/
rc = sendto(
SapSocket, /* Socket */
Requestp, /* Ptr to send buffer */
ReturnLength, /* Length of data */
0, /* Flags */
(PSOCKADDR)Addrptr, /* Ptr to address */
Addrlength); /* Length of address */
/** If error - handle it **/
if (rc == -1) {
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: GETNEAREST: Sendto failed: error = %d\n", h_errno));
}
}
/**
Goto next buffer to send.
Since this is on a WAN, we do not have to do the
55ms delay.
**/
Requestp += ReturnLength;
}
/**
We have already sent the responses. So we return 0
here to signify that there is no reply to be sent.
**/
ReturnLength = 0;
break;
/** Advertise packets **/
case SAPTYPE_GENERAL_SERVICE_RESPONSE:
/** Get rid of the query type from the packet **/
Requestp += sizeof(USHORT);
Length -= sizeof(USHORT);
/**
Get the card number. If we only have one card
then we use that card for everything.
**/
Cardnum = SapGetCardFromAddress(Addrptr, &WanFlag, NetNum);
/** If error or from SAP Agent - ignore **/
if ((Cardnum == CARDRET_UNKNOWN) || (Cardnum == CARDRET_MYSELF))
return 0;
/**
Go thru the packet and update all the server entries
in there.
**/
while (Length >= SAP_HEADER_SIZE) {
/** **/
Servp = (PSAP_HEADER)Requestp;
/**
If the advertised network number is 0, replace it
with the network number of the card.
**/
if (!memcmp(Servp->Address, SapZeros, SAP_NET_LEN)) {
memcpy(Servp->Address, NetNum, SAP_NET_LEN);
}
/** Update this entry **/
SdmdUpdateEntry(
Servp->ServerName,
ntohs(Servp->ServerType),
Servp->Address,
ntohs(Servp->Hopcount),
Cardnum,
Addrptr + 6, /* Node addr of who sent the packet */
WanFlag);
/** Goto the next entry **/
Requestp += SAP_HEADER_SIZE;
Length -= SAP_HEADER_SIZE;
}
/** There is no return value here **/
ReturnLength = 0;
break;
/**
We should never get these. These are only valid as responses
to a NEAREST_SERVICE_QUERY packet. Just drop thru to default.
**/
case SAPTYPE_NEAREST_SERVICE_RESPONSE: /* DROP THRU TO DEFAULT */
/** Unknown - just ignore the packet **/
default:
ReturnLength = 0;
break;
}
/** Return the length of data to send back **/
return ReturnLength;
}
/*++
*******************************************************************
S a p S t a r t R e c v T h r e a d
Routine Description:
This routine starts a receive thread.
Arguments:
Nothing
Return Value:
0 = OK
1 = Error
*******************************************************************
--*/
INT
SapStartRecvThread(
VOID)
{
HANDLE Handle;
DWORD Threadid;
DWORD Error;
/**
We count this thread as running before we start it
for synchronization. If the start fails - then we will
dec this count
**/
SAP_INC_THREAD_COUNT("Receive Start", &SapCurReceiveThreads);
/** Start the thread **/
Handle = CreateThread(
NULL, /* Security Ptr */
0, /* Initial stack size */
SapRecvThread, /* Thread Function */
(LPVOID)SapSocket, /* Parm for the thread */
0, /* Creation Flags */
&Threadid); /* Ptr to thread id */
if (Handle == NULL) {
/** **/
Error = GetLastError();
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: Error starting receive thread = %d\n", Error));
}
/** Dec the thread count **/
SAP_DEC_THREAD_COUNT("Receive Start Error", &SapCurReceiveThreads);
/** Log the error **/
SsLogEvent(
NWSAP_EVENT_STARTRECEIVE_ERROR,
0,
NULL,
Error);
/** Return Error **/
return 1;
}
/** We can close this handle **/
CloseHandle(Handle);
/** Return OK **/
return 0;
}
/*++
*******************************************************************
S a p S t a r t W o r k e r T h r e a d
Routine Description:
This routine starts a worker thread.
Arguments:
None
Return Value:
0 = OK
1 = Error
*******************************************************************
--*/
INT
SapStartWorkerThread(
VOID)
{
HANDLE Handle;
DWORD Threadid;
DWORD Error;
/**
We count this thread as running before we start it
for synchronization. If the start fails - then we will
dec this count
**/
SAP_INC_THREAD_COUNT("Worker Start", &SapCurWorkerThreads);
/** Start the thread **/
Handle = CreateThread(
NULL, /* Security Ptr */
0, /* Initial stack size */
SapWorkerThread, /* Thread Function */
(LPVOID)SapSocket, /* Parm for the thread */
0, /* Creation Flags */
&Threadid); /* Ptr to thread id */
/**
CreateThread failed - dec the count and return the error.
**/
if (Handle == NULL) {
/** **/
Error = GetLastError();
IF_DEBUG(ERRORS) {
SS_PRINT(("NWSAP: Error starting worker thread = %d\n", Error));
}
/** Uncount this thread running **/
SAP_DEC_THREAD_COUNT("Worker Start Error", &SapCurWorkerThreads);
/** Log the error to the event log **/
SsLogEvent(
NWSAP_EVENT_STARTWORKER_ERROR,
0,
NULL,
Error);
/** Return Error **/
return 1;
}
/** We can close this handle **/
CloseHandle(Handle);
/** Return OK **/
return 0;
}
/*++
*******************************************************************
S a p G e t N e a r e s t F r o m S e n d L i s t
Routine Description:
When we get a Nearest Service Request from the network,
then first place we look is our advertise list. If
we find an entry here, we build the return packet.
Arguments:
Bufferp = Ptr to buffer to build in
ServerType = Type of server being requested
Return Value:
Length of the response.
0 = There is no packet to send back. The server type
is not in our send list.
*******************************************************************
--*/
INT
SapGetNearestFromSendList(
PUCHAR Bufferp,
USHORT ServerType)
{
PSAP_SERVER Servp;
PSAP_HEADER Hdrp;
/** Convert back to network order **/
ServerType = ntohs(ServerType);
/** Get access to the send list **/
ACQUIRE_SENDTABLE_LOCK();
/** Go find the server type in the list **/
Servp = SapServHead;
while (Servp) {
/** If entry being deleted - skip it **/
if (Servp->Hopcount == htons(16)) {
Servp = Servp->Next;
continue;
}
/** If server does not want to be used - skip it **/
if (Servp->RespondNearest == FALSE) {
Servp = Servp->Next;
continue;
}
/** If this is the entry we want - break out **/
if (Servp->ServerType == ServerType)
break;
/** Goto the next entry **/
Servp = Servp->Next;
}
/** If no entry found - return 0 **/
if (Servp == NULL) {
RELEASE_SENDTABLE_LOCK();
return 0;
}
/** Build the return entry in the buffer given **/
*(PUSHORT)Bufferp = htons(SAPTYPE_NEAREST_SERVICE_RESPONSE);
Bufferp += sizeof(USHORT);
Hdrp = (PSAP_HEADER)Bufferp;
Hdrp->ServerType = Servp->ServerType;
SAP_COPY_SERVNAME(Hdrp->ServerName, Servp->ServerName);
SAP_COPY_ADDRESS(Hdrp->Address, Servp->Address);
Hdrp->Hopcount = Servp->Hopcount;
/** Release the lock on the list **/
RELEASE_SENDTABLE_LOCK();
/** Return the length to send back **/
return SAP_HEADER_SIZE+sizeof(USHORT);
}
/*++
*******************************************************************
S a p C a n I R e s p o n d N e a r e s t
Routine Description:
For a given ServerName and SeverType see if it is allowed
to respond to a Find Nearest call for it. This is called
from the RespondNearestWan code to ask if an entry that is
in our table can be responded with.
It is very much assumed that this entry is in our advertise
table. If it is not found in the table we return NO.
NOTE: THE SENDTABLE LOCK MUST BE HELD WHEN WE GET HERE
Arguments:
ServerName = Ptr to 48 byte name to look for
ServerType = Object type to look for
Return Value:
TRUE = Yes - respond with it
FALSE = No - do not respond with it
*******************************************************************
--*/
BOOL
SapCanIRespondNearest(
PUCHAR ServerName,
USHORT ServerType)
{
PSAP_SERVER Servp;
/** Convert back to network order **/
ServerType = ntohs(ServerType);
/** Go find the server type in the list **/
Servp = SapServHead;
while (Servp) {
/** If entry being deleted - skip it **/
if (Servp->Hopcount == htons(16)) {
Servp = Servp->Next;
continue;
}
/** If server does not want to be used - skip it **/
if (Servp->RespondNearest == FALSE) {
Servp = Servp->Next;
continue;
}
/**
If this is the right type and the name
matches - then break out of the loop to return
OK.
**/
if ((Servp->ServerType == htons(ServerType)) &&
(!SAP_NAMECMP(ServerName, Servp->ServerName))) {
break;
}
/** Goto the next entry **/
Servp = Servp->Next;
}
/** If no entry found - return cannot respond **/
if (Servp == NULL)
return FALSE;
/** Tell the caller he can respond with this server **/
return TRUE;
}
/*++
*******************************************************************
S a p G e t C a r d F r o m A d d r e s s
Routine Description:
When we receive a packet, we need to know which card we
received it on.
Arguments:
Addrptr = Ptr to address that we received from
WanFlagp = Ptr to BOOL to store WAN status of the card
NetNump = Ptr to store network number of the card
Return Value:
The card number that we received on
CARDRET_UNKNOWN = 0xFFFD = Cannot find card this is for.
CARDRET_MYSELF = 0xFFFE = It was from our SAP Agent (We sent it)
CARDRET_INTERNAL = 0xFFFF = It is from this machine but some other process.
*******************************************************************
--*/
INT
SapGetCardFromAddress(
PUCHAR Addrptr,
BOOL *WanFlagp,
PUCHAR NetNump)
{
INT Cardnum;
PSAP_CARD Cardptr;
/** Get the lock on the card list **/
Cardnum = 0;
ACQUIRE_CARDLIST_READERS_LOCK("GetCardFromAddress");
Cardptr = SapCardHead;
/** Find the card this belongs to **/
while (Cardptr) {
/** If this is it - break out **/
if (!memcmp(Cardptr->Netnum, Addrptr+2, SAP_NET_LEN))
break;
/** Goto the next card **/
Cardnum++;
Cardptr = Cardptr->Next;
}
/** If not from any card - toss it **/
if (Cardptr == NULL) {
/**
If the netnum is zero and we have only one card then
we just use the first card.
**/
if (!memcmp(Addrptr + 2, SapZeros, SAP_NET_LEN)) {
if (SapNumCards == 1) {
Cardptr = SapCardHead;
goto GotCard;
}
}
/** Release the lock **/
RELEASE_CARDLIST_READERS_LOCK("GetCardFromAddress1");
/** Print the error **/
#if DBG
IF_DEBUG(NOCARD) {
SS_PRINT(("NWSAP: HANREQ: no card for address\n"));
SapDumpMem(Addrptr, 15, "BAD ADDRESS");
}
#endif
/** Return the error **/
return CARDRET_UNKNOWN;
}
/** Set the WAN Status and Netnum if user wanted **/
GotCard:
*WanFlagp = Cardptr->Wanflag;
if (NetNump) {
SAP_COPY_NETNUM(NetNump, Cardptr->Netnum);
}
/**
If this is from my local machine - then we want
to check to see if we was sent by me (The SAP AGENT),
or by some other process.
**/
if (!memcmp(Cardptr->Nodenum, Addrptr+6, SAP_NODE_LEN)) {
USHORT Socket;
/** **/
Socket = ntohs(*(PUSHORT)(Addrptr + 12));
/** This is from SAP Agent - return it **/
if (Socket == NWSAP_SAP_SOCKET) {
RELEASE_CARDLIST_READERS_LOCK("GetCardFromAddress2");
return CARDRET_MYSELF;
}
/** We got one from some other in this machine **/
RELEASE_CARDLIST_READERS_LOCK("GetCardFromAddress3");
return CARDRET_INTERNAL;
}
/** Return the card number **/
RELEASE_CARDLIST_READERS_LOCK("GetCardFromAddress4");
return Cardnum;
}