windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/svcloc/svccli.cxx
2020-09-26 16:20:57 +08:00

2980 lines
68 KiB
C++

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
svccli.cxx
Abstract:
Contains client side code of service location protocol.
Author:
Madan Appiah (madana) 15-May-1995
Environment:
User Mode - Win32
Revision History:
--*/
#include <svcloc.hxx>
//
// This is the IPX address we send to
//
BYTE GlobalSapBroadcastAddress[] = {
AF_IPX, 0, // Address Family
0x00, 0x00, 0x00, 0x00, // Dest. Net Number
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Dest. Node Number
0x04, 0x52, // Dest. Socket
0x04 // Packet type
};
VOID
GetNBUniqueName(
LPSTR NBNameBuf,
DWORD NBNameBufLen
)
{
LPSTR NamePtr = NBNameBuf;
LPSTR NameEndPtr = NBNameBuf + NBNameBufLen;
TcpsvcsDbgAssert( NBNameBufLen >
sizeof(NETBIOS_INET_UNIQUE_NAME) + 1 );
memcpy(
NamePtr,
NETBIOS_INET_UNIQUE_NAME,
NETBIOS_INET_UNIQUE_NAME_LEN
);
NamePtr += NETBIOS_INET_UNIQUE_NAME_LEN;
//
// now append a random char.
//
DWORD RandNum;
RandNum = (DWORD)rand();
RandNum = RandNum % (26 + 10); // 26 alphabets, and 10 numerics
if( RandNum < 10 ) {
*NamePtr = (CHAR)('0'+ RandNum);
}
else {
*NamePtr = (CHAR)('A'+ RandNum - 10);
}
NamePtr++;
TcpsvcsDbgAssert( GlobalComputerName[0] != '\0' );
//
// append computer name.
//
DWORD ComputerNameLen = strlen( GlobalComputerName );
if( ComputerNameLen < (DWORD)(NameEndPtr - NamePtr) ) {
memcpy(
NamePtr,
GlobalComputerName,
ComputerNameLen );
NamePtr += ComputerNameLen;
//
// fill the trailing chars with spaces.
//
memset( NamePtr, ' ', DIFF(NameEndPtr - NamePtr) );
}
else {
memcpy(
NamePtr,
GlobalComputerName,
DIFF(NameEndPtr - NamePtr) );
}
return;
}
DWORD
ProcessSapResponses(
PLIST_ENTRY ResponseList,
LPSAP_ADDRESS_INFO *SapAddresses,
DWORD *NumSapAddresses
)
{
DWORD Error;
PLIST_ENTRY Response;
DWORD NumResponses;
DWORD Size;
LPSAP_ADDRESS_INFO Addresses = NULL;
LPSAP_ADDRESS_INFO Address;
//
// compute number of entries in the list.
//
NumResponses = 0;
for( Response = ResponseList->Flink;
Response != ResponseList;
Response = Response->Flink ) {
NumResponses++;
}
TcpsvcsDbgAssert( NumResponses != 0 );
if( NumResponses == 0 ) {
*SapAddresses = NULL;
*NumSapAddresses = 0;
return( ERROR_SUCCESS );
}
//
// allocate memory for return buffer.
//
Size = sizeof( SAP_ADDRESS_INFO ) * NumResponses;
Addresses = (LPSAP_ADDRESS_INFO)
SvclocHeap->Alloc( Size );
if( Addresses == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
for( Address = Addresses, Response = ResponseList->Flink;
Response != ResponseList;
Address++, Response = Response->Flink ) {
TcpsvcsDbgAssert( (LPBYTE)Address < (LPBYTE)Addresses + Size );
*Address = ((LPSAP_ADDRESS_ENTRY)Response)->Address;
}
*SapAddresses = Addresses;
*NumSapAddresses = NumResponses;
Addresses = NULL;
Error = ERROR_SUCCESS;
Cleanup:
if( Addresses != NULL ) {
SvclocHeap->Free( Addresses );
}
return( Error );
}
DWORD
ProcessSingleSapResponse(
LPSAP_IDENT_HEADER SapResponse,
PLIST_ENTRY ResponsesList
)
{
PLIST_ENTRY Response;
CHAR ServerName[MAX_COMPUTERNAME_LENGTH + 1];
LPSTR ServerNamePtr;
LPBYTE ServerAddress;
TcpsvcsDbgAssert(SapResponse->ServerType == htons(INTERNET_SERVICE_SAP_ID));
//
// check GUID portion of the server name.
//
if( memcmp(
SapResponse->ServerName +
MAX_COMPUTERNAME_LENGTH,
SERVICE_GUID_STR,
32 ) != 0 ) {
//
// ignore this response.
//
TcpsvcsDbgPrint((
DEBUG_ERRORS,
"GetSapAddress() received unknown (GUID) response.\n" ));
return( ERROR_SUCCESS );
}
//
// server name portion.
//
memcpy(
ServerName,
SapResponse->ServerName,
MAX_COMPUTERNAME_LENGTH );
ServerName[MAX_COMPUTERNAME_LENGTH] = '\0';
//
// chop off padding.
//
ServerNamePtr = ServerName;
while ( *ServerNamePtr != '\0' ) {
if( *ServerNamePtr == '!' ) {
*ServerNamePtr = '\0';
break;
}
ServerNamePtr++;
}
ServerAddress = SapResponse->Address;
//
// find out if the server entry is already in the existing list.
//
for( Response = ResponsesList->Flink;
Response != ResponsesList;
Response = Response->Flink ) {
LPSAP_ADDRESS_ENTRY ResponseEntry;
ResponseEntry = (LPSAP_ADDRESS_ENTRY)Response;
if( memcmp(
ResponseEntry->Address.Address,
ServerAddress,
IPX_ADDRESS_LENGTH ) == 0 ) {
return( ERROR_SUCCESS );
}
if( strcmp(
ResponseEntry->Address.ServerName,
ServerName ) == 0 ) {
return( ERROR_SUCCESS );
}
}
//
// we have unique new entry.
//
//
// allocate memory for the new response.
//
LPSAP_ADDRESS_ENTRY NewResponse;
NewResponse = (LPSAP_ADDRESS_ENTRY)
SvclocHeap->Alloc( sizeof(SAP_ADDRESS_ENTRY) );
if( NewResponse == NULL ) {
return( ERROR_NOT_ENOUGH_MEMORY );
}
strcpy( (LPSTR)NewResponse->Address.ServerName, ServerName );
memcpy(
NewResponse->Address.Address,
ServerAddress,
IPX_ADDRESS_LENGTH );
NewResponse->Address.HopCount = ntohs(SapResponse->HopCount);
//
// add this new entry to the list.
//
InsertTailList( ResponsesList, &NewResponse->Next );
return( ERROR_SUCCESS );
}
DWORD
GetSapAddress(
LPSAP_ADDRESS_INFO *SapAddresses,
DWORD *NumSapAddresses
)
/*++
Routine Description:
This routine discovers all IPX servers by sending sap broadcast and
filtering responses that match our GUID.
Arguments:
SapAddresses - pointer to a location where the pointer to the
addresses buffer is returned. The caller should free this buffer
after use.
NumSapAddresses - pointer a location where the number of addresses
returned in the above buffer is returned.
Return Value:
Windows Error Code.
--*/
{
DWORD Error = ERROR_SUCCESS;
SOCKET SapSocket;
SOCKADDR_IPX SapSocketAddr;
SAP_REQUEST SapRequest;
BYTE DestAddr[SAP_ADDRESS_LENGTH];
LIST_ENTRY ResponsesList;
InitializeListHead(&ResponsesList);
//
// create an IPX socket.
//
SapSocket = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );
if ( SapSocket == INVALID_SOCKET ) {
Error = WSAGetLastError();
goto Cleanup;
}
//
// Set the socket to non-blocking
//
DWORD NonBlocking;
NonBlocking = 1;
if ( ioctlsocket( SapSocket, FIONBIO, &NonBlocking ) ==
SOCKET_ERROR ) {
Error = WSAGetLastError();
goto Cleanup;
}
//
// Allow sending of broadcasts
//
DWORD Value;
Value = 1;
if ( setsockopt( SapSocket,
SOL_SOCKET,
SO_BROADCAST,
(PCHAR) &Value,
sizeof(INT)) == SOCKET_ERROR ) {
Error = WSAGetLastError();
goto Cleanup;
}
//
// Bind the socket
//
memset( &SapSocketAddr, 0, sizeof( SOCKADDR_IPX));
SapSocketAddr.sa_family = AF_IPX;
SapSocketAddr.sa_socket = 0; // no specific port
if ( bind( SapSocket,
(PSOCKADDR) &SapSocketAddr,
sizeof( SOCKADDR_IPX)) == SOCKET_ERROR ) {
Error = WSAGetLastError();
goto Cleanup;
}
//
// Set the extended address option
//
Value = 1;
if ( setsockopt( SapSocket, // Socket Handle
NSPROTO_IPX, // Option Level
IPX_EXTENDED_ADDRESS, // Option Name
(PCHAR)&Value, // Ptr to on/off flag
sizeof(INT)) == SOCKET_ERROR ) {
// Length of flag
Error = WSAGetLastError();
goto Cleanup;
}
//
// Send the Sap query service packet
//
SapRequest.QueryType = htons( 1 ); // General Service Query
SapRequest.ServerType = htons(INTERNET_SERVICE_SAP_ID);
//
// Set the address to send to
//
memcpy( DestAddr, GlobalSapBroadcastAddress, SAP_ADDRESS_LENGTH );
//
// We will send out SAP requests 3 times and wait 1 sec for
// Sap responses the first time, 1 sec the second and 3 sec the
// third time. Once we get MAXSITES responses, we give up.
// We also give up if after processing all replies from a send,
// we find at least one DC in the search domain.
//
DWORD i;
#define SAP_DISCOVERY_RETRIES 1
for ( i = 0; i < SAP_DISCOVERY_RETRIES; i++ ) {
DWORD StartTickCount;
DWORD TimeOut = (i + 1) * 1000;
//
// Send the packet out
//
if ( sendto( SapSocket,
(PCHAR) &SapRequest,
sizeof( SapRequest ),
0,
(PSOCKADDR) DestAddr,
SAP_ADDRESS_LENGTH ) == SOCKET_ERROR ) {
Error = WSAGetLastError();
goto Cleanup;
}
//
// Loop for incoming packets
//
StartTickCount = GetTickCount();
do {
LPSAP_IDENT_HEADER SapResponse;
DWORD BytesReceived;
BYTE RecvBuffer[SAP_MAXRECV_LENGTH];
Sleep(50); // take a deep breath
BytesReceived =
recvfrom( SapSocket,
(PCHAR)RecvBuffer,
SAP_MAXRECV_LENGTH,
0,
NULL,
NULL );
if ( BytesReceived == SOCKET_ERROR ) {
Error = WSAGetLastError();
if ( Error == WSAEWOULDBLOCK ) {
//
// no data on socket, continue looping
//
Error = ERROR_SUCCESS;
continue;
}
}
if ( Error != ERROR_SUCCESS ) {
goto Cleanup;
}
//
// if socket closed, return.
//
if ( BytesReceived == 0 ) {
goto ProcessResponse;
}
//
// Skip over query type
//
BytesReceived -= sizeof(USHORT);
SapResponse = (LPSAP_IDENT_HEADER)
((LPBYTE)RecvBuffer + sizeof(USHORT));
//
// loop through and check all addresses.
//
while ( BytesReceived >= sizeof( SAP_IDENT_HEADER )) {
Error = ProcessSingleSapResponse(
SapResponse,
&ResponsesList );
if( Error != ERROR_SUCCESS ) {
goto Cleanup;
}
BytesReceived -= sizeof( SAP_IDENT_HEADER );
SapResponse++;
}
} while((GetTickCount() - StartTickCount) < TimeOut );
}
//
// Process response list.
//
ProcessResponse :
Error = ProcessSapResponses(
&ResponsesList,
SapAddresses,
NumSapAddresses );
if( Error != ERROR_SUCCESS ) {
goto Cleanup;
}
//
// we are done.
//
Error = ERROR_SUCCESS;
Cleanup:
closesocket( SapSocket );
//
// free response list if any.
//
while( !IsListEmpty( &ResponsesList ) ) {
PLIST_ENTRY ListEntry;
//
// remove the head entry and free.
//
ListEntry = RemoveHeadList( &ResponsesList );
SvclocHeap->Free( ListEntry );
}
return( Error );
}
DWORD
DiscoverIpxServers(
LPSTR ServerName
)
/*++
Routine Description:
This routine discovers all IPX servers using RNR GetAddressByName.
Assume : WSAStartup is called before calling this function.
Arguments:
ServerName : if this value is set non-NULL then discover only the
specified server otherwise diescover all.
Return Value:
Windows Error Code.
--*/
{
DWORD Error;
DWORD NumSapAddresses = 0;
LPSAP_ADDRESS_INFO SapAddresses = NULL;
SOCKADDR DestAddr;
//
// first find out all servers running IPX only.
// performed by doing SAP broadcast.
//
Error = GetSapAddress( &SapAddresses, &NumSapAddresses );
if( Error != ERROR_SUCCESS ) {
goto Cleanup;
}
//
// now process returned buffer entries.
//
DWORD i;
TcpsvcsDbgAssert( NumSapAddresses > 0 );
//
// create a IPX socket to send query message, if it is not created
// before.
//
LOCK_SVC_GLOBAL_DATA();
if( GlobalCliIpxSocket == INVALID_SOCKET ) {
SOCKADDR_IPX IpxSocketAddr;
DWORD Arg;
GlobalCliIpxSocket = socket( PF_IPX, SOCK_DGRAM, NSPROTO_IPX );
if( GlobalCliIpxSocket == INVALID_SOCKET ) {
Error = WSAGetLastError();
UNLOCK_SVC_GLOBAL_DATA();
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"socket() failed, %ld\n", Error ));
goto Cleanup;
}
//
// make this socket non blocking.
//
Arg = 1;
if( (ioctlsocket( GlobalCliIpxSocket, FIONBIO, &Arg )) == SOCKET_ERROR ) {
Error = WSAGetLastError();
UNLOCK_SVC_GLOBAL_DATA();
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"ioctlsocket() failed, %ld\n", Error ));
goto Cleanup;
}
//
// Bind the socket
//
memset( &IpxSocketAddr, 0, sizeof( SOCKADDR_IPX));
IpxSocketAddr.sa_family = AF_IPX;
IpxSocketAddr.sa_socket = 0; // no specific port
if ( bind( GlobalCliIpxSocket,
(PSOCKADDR) &IpxSocketAddr,
sizeof( SOCKADDR_IPX)) == SOCKET_ERROR ) {
Error = WSAGetLastError();
UNLOCK_SVC_GLOBAL_DATA();
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"bind() failed, %ld\n", Error ));
goto Cleanup;
}
//
// add this socket to global list.
//
FD_SET( GlobalCliIpxSocket, &GlobalCliSockets );
}
UNLOCK_SVC_GLOBAL_DATA();
LPSAP_ADDRESS_INFO SapAddressInfo;
SapAddressInfo = SapAddresses;
DestAddr.sa_family = AF_IPX;
for( i = 0; i < (DWORD)NumSapAddresses; i++, SapAddressInfo++ ) {
//
// if we are asked to discover only specific server
// check the server name first before sending the query
// message.
//
if( ServerName != NULL ) {
if( _stricmp(
ServerName,
SapAddressInfo->ServerName ) != 0 ) {
continue;
}
}
//
// send query message to each discovered server.
//
memcpy(
DestAddr.sa_data,
SapAddressInfo->Address,
IPX_ADDRESS_LENGTH );
Error = sendto(
GlobalCliIpxSocket,
(LPCSTR)GlobalCliQueryMsg,
GlobalCliQueryMsgLen,
0,
&DestAddr,
2 + IPX_ADDRESS_LENGTH );
if( Error == SOCKET_ERROR ) {
Error = WSAGetLastError();
TcpsvcsDbgPrint(( DEBUG_ERRORS, "sendto() failed, %ld\n", Error ));
continue;
}
}
//
// DONE.
//
Error = ERROR_SUCCESS;
Cleanup:
if( SapAddresses != NULL ) {
SvclocHeap->Free( SapAddresses );
}
if( Error != ERROR_SUCCESS ) {
TcpsvcsDbgPrint(( DEBUG_ERRORS, "DiscoverIpxServers failed, %ld\n", Error ));
}
return( Error );
}
DWORD
DiscoverIpServers(
LPSTR ServerName
)
/*++
Routine Description:
This function sends out a discovery message to all netbios lanas.
Arguments:
UniqueServerName : pointer to a server name string. If this pointer is
NULL, it uses IC group name to discover all IP servers, otherwise
it discovers the bindings of the specified server.
Return Value:
Windows Error Code.
--*/
{
DWORD Error;
SOCKADDR_NB RemoteSocketAddress;
LOCK_SVC_GLOBAL_DATA();
if( GlobalCliNBSockets.fd_count == 0 ) {
SOCKET s;
DWORD Lana;
SOCKADDR_NB SocketAddress;
// make netbios source address.
//
memset( &SocketAddress, 0x0, sizeof(SOCKADDR_NB) );
SocketAddress.snb_family = AF_NETBIOS;
SocketAddress.snb_type = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
GetNBUniqueName(
SocketAddress.snb_name,
sizeof(SocketAddress.snb_name) );
//
// enumurate lanas first.
//
LANA_ENUM Lanas;
if( GetEnumNBLana( &Lanas ) ) {
//
// try only the lanas that are returned.
//
DWORD i;
for( i = 0; i < Lanas.length; i++ ) {
if ( MakeNBSocketForLana(
Lanas.lana[i],
(PSOCKADDR)&SocketAddress,
&s ) ) {
//
// successfully made another socket, add to global list.
//
FD_SET( s, &GlobalCliNBSockets );
FD_SET( s, &GlobalCliSockets );
}
}
}
else {
UCHAR Lana;
//
// try all possible lanas and accept all valid lana sockets.
//
for( Lana = 0; Lana < MAX_LANA ; Lana-- ) {
if ( MakeNBSocketForLana(
Lana,
(PSOCKADDR)&SocketAddress,
&s ) ) {
//
// successfully made another socket, add to global
// lists.
//
FD_SET( s, &GlobalCliNBSockets );
FD_SET( s, &GlobalCliSockets );
}
}
}
}
UNLOCK_SVC_GLOBAL_DATA();
if( GlobalCliNBSockets.fd_count == 0 ) {
return( ERROR_SUCCESS );
}
//
//
// make netbios destination address.
//
memset( &RemoteSocketAddress, 0x0, sizeof(SOCKADDR_NB) );
if( ServerName == NULL ) {
//
// if no server name is specified, then send the discovery message
// to IC group name.
//
RemoteSocketAddress.snb_family = AF_NETBIOS;
RemoteSocketAddress.snb_type = TDI_ADDRESS_NETBIOS_TYPE_GROUP;
TcpsvcsDbgAssert(
sizeof(RemoteSocketAddress.snb_name) >=
NETBIOS_INET_GROUP_NAME_LEN );
memcpy(
RemoteSocketAddress.snb_name,
NETBIOS_INET_GROUP_NAME,
NETBIOS_INET_GROUP_NAME_LEN );
}
else {
DWORD ServerNameLen;
//
// send the discovery message to the specified server.
//
RemoteSocketAddress.snb_family = AF_NETBIOS;
RemoteSocketAddress.snb_type = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
ServerNameLen = strlen(ServerName);
TcpsvcsDbgAssert(
ServerNameLen <=
sizeof(RemoteSocketAddress.snb_name) );
memcpy(
RemoteSocketAddress.snb_name,
ServerName,
(ServerNameLen >= sizeof(RemoteSocketAddress.snb_name)) ?
sizeof(RemoteSocketAddress.snb_name) :
ServerNameLen );
}
//
// send message to all lanas.
//
DWORD i;
for( i = 0; i < GlobalCliNBSockets.fd_count ; i++ ) {
//
// now send query message.
//
Error = sendto(
GlobalCliNBSockets.fd_array[i],
(LPCSTR)GlobalCliQueryMsg,
GlobalCliQueryMsgLen,
0,
(PSOCKADDR)&RemoteSocketAddress,
sizeof(RemoteSocketAddress) );
if( Error == SOCKET_ERROR ) {
Error = WSAGetLastError();
TcpsvcsDbgPrint(( DEBUG_ERRORS, "sendto() failed, %ld\n", Error ));
}
}
return( ERROR_SUCCESS );
}
DWORD
ProcessSvclocQueryResponse(
SOCKET ReceivedSocket,
LPBYTE ReceivedMessage,
DWORD ReceivedMessageLength,
SOCKADDR *SourcesAddress,
DWORD SourcesAddressLength
)
/*++
Routine Description:
This function processes the query response message and queues them to
the global list.
Arguments:
ReceivedSocket - socket the message came from.
ReceivedMessage - pointer to a message buffer.
ReceivedMessageLength - length of the above message.
SourcesAddress - address of the sender.
SourcesAddressLength - length of the above address.
Return Value:
Windows Error Code.
--*/
{
DWORD Error;
DWORD NodeLength;
LPCLIENT_QUERY_RESPONSE ResponseNode;
PLIST_ENTRY NextResponse;
LPBYTE EndReceiveMessage;
//
// validate this message.
//
//
// first DWORD in the message is the length of the message.
//
if( *(DWORD *)ReceivedMessage != ReceivedMessageLength ) {
return( ERROR_INVALID_PARAMETER );
}
//
// last DWORD is terminating char.
//
EndReceiveMessage = ReceivedMessage + ReceivedMessageLength;
if( *(DWORD *) (EndReceiveMessage - sizeof(DWORD)) != 0xFFFFFFFF ) {
return( ERROR_INVALID_PARAMETER );
}
//
// validate check sum.
// last but one DWORD is check of the message.
//
DWORD CheckSum;
CheckSum = *(DWORD *) (EndReceiveMessage - 2 * sizeof(DWORD));
DWORD ComputedCheckSum;
ComputedCheckSum =
ComputeCheckSum(
ReceivedMessage + sizeof(DWORD), // skip length field.
ReceivedMessageLength - 3 * sizeof(DWORD) );
// skip last 2 DWORDS too.
if( ComputedCheckSum != CheckSum ) {
return( ERROR_INVALID_PARAMETER );
}
//
// check version number.
//
INET_VERSION_NUM *VersionNum;
VersionNum = (INET_VERSION_NUM *)(ReceivedMessage + sizeof(DWORD));
if( VersionNum->Version.Major != INET_MAJOR_VERSION ) {
return( ERROR_INVALID_PARAMETER );
}
LOCK_SVC_GLOBAL_DATA();
//
// check to see, there is already a response from this server, if so
// update the response message, otherwise add a new entry.
//
for( NextResponse = GlobalCliQueryRespList.Flink;
NextResponse != &GlobalCliQueryRespList;
NextResponse = NextResponse->Flink ) {
LPCLIENT_QUERY_RESPONSE QueryResponse;
QueryResponse = (LPCLIENT_QUERY_RESPONSE)NextResponse;
//
// if either the server addresses match or
// the server names in the message field match, then this
// must be duplicate, so replace with old one.
//
if(
#if 0
//
// this check always succeseed for group name.
//
( (SourcesAddressLength == QueryResponse->SourcesAddressLength ) && (memcmp(
SourcesAddress,
QueryResponse->SourcesAddress,
SourcesAddressLength ) == 0 ) ) ||
#endif // 0
( strcmp(
(LPSTR)ReceivedMessage + 2 * sizeof(DWORD),
// skip length and version DWORDS.
(LPSTR)QueryResponse->ResponseBuffer + 2 * sizeof(DWORD) )
== 0 ) ) {
//
// free old response buffer.
//
SvclocHeap->Free( QueryResponse->ResponseBuffer );
//
// copy new response pointer.
//
QueryResponse->ResponseBuffer = ReceivedMessage;
QueryResponse->ResponseBufferLength = ReceivedMessageLength;
QueryResponse->TimeStamp = time( NULL );
//
// done.
//
Error = ERROR_SUCCESS;
goto Cleanup;
}
}
//
// brand new response.
//
NodeLength = sizeof(CLIENT_QUERY_RESPONSE) + SourcesAddressLength;
ResponseNode = (LPCLIENT_QUERY_RESPONSE)SvclocHeap->Alloc( NodeLength );
if( ResponseNode == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// setup sock address pointer.
//
ResponseNode->SourcesAddress = (SOCKADDR *)(ResponseNode + 1);
//
// copy address data.
//
memcpy(
ResponseNode->SourcesAddress,
SourcesAddress,
SourcesAddressLength );
//
// copy other fields.
//
ResponseNode->ReceivedSocket = ReceivedSocket;
ResponseNode->ResponseBuffer = ReceivedMessage;
ResponseNode->ResponseBufferLength = ReceivedMessageLength;
ResponseNode->SourcesAddressLength = SourcesAddressLength;
ResponseNode->TimeStamp = time( NULL );
//
// link this entry to global list.
//
InsertTailList( &GlobalCliQueryRespList, &ResponseNode->NextEntry );
//
// done.
//
Error = ERROR_SUCCESS;
Cleanup:
UNLOCK_SVC_GLOBAL_DATA();
return( Error );
}
DWORD
ReceiveResponses(
WORD Timeout,
BOOL WaitForAllResponses
)
/*++
Routine Description:
This routine receives responses for the query messages sent out to the
servers.
Arguments:
Time - Time to wait for the response messages in secs.
WaitForAllResponses : If this flag is set TRUE, this function wait
complete 'Time' secs for all responses to arrive. Otherwise it
will return after a succcessful response is received.
Return Value:
None.
--*/
{
DWORD Error;
struct timeval SockTimeout;
struct timeval *pSockTimeout;
DWORD TimeoutinMSecs = INFINITE;
DWORD TickCountStart;
if( Timeout != 0 ) {
TimeoutinMSecs = Timeout * 1000;
pSockTimeout = &SockTimeout;
}
else {
pSockTimeout = NULL;
}
//
// now loop for incoming messages.
//
//
// remember current tick out.
//
TickCountStart = GetTickCount();
for( ;; ) {
fd_set ReadSockets;
INT NumReadySockets;
DWORD NumSockets;
DWORD i;
LOCK_SVC_GLOBAL_DATA();
NumSockets = GlobalCliSockets.fd_count;
UNLOCK_SVC_GLOBAL_DATA();
if( NumSockets != 0 ) {
//
// select
//
LOCK_SVC_GLOBAL_DATA();
memcpy( &ReadSockets, &GlobalCliSockets, sizeof(fd_set) );
UNLOCK_SVC_GLOBAL_DATA();
//
// compute time out, if it is not infinity.
//
if( pSockTimeout != NULL ) {
SockTimeout.tv_sec = TimeoutinMSecs / 1000;
SockTimeout.tv_usec = TimeoutinMSecs % 1000;
}
NumReadySockets =
select(
0, // compatibility argument, ignored.
&ReadSockets, // sockets to test for readability.
NULL, // no write wait
NULL, // no error check.
pSockTimeout ); // NO timeout.
if( NumReadySockets == SOCKET_ERROR ) {
//
// when all sockets are closed, we are asked to return or
// something else has happpened which we can't handle.
//
Error = WSAGetLastError();
goto Cleanup;
}
TcpsvcsDbgAssert( (DWORD)NumReadySockets == ReadSockets.fd_count );
for( i = 0; i < (DWORD)NumReadySockets; i++ ) {
DWORD ReadMessageLength;
struct sockaddr_nb SourcesAddress;
int SourcesAddressLength;
LPBYTE RecvBuffer;
DWORD RecvBufferLength;
RecvBufferLength = SVCLOC_CLI_QUERY_RESP_BUF_SIZE;
RecvBuffer = (LPBYTE)SvclocHeap->Alloc( RecvBufferLength );
if( RecvBuffer == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// read next message.
//
SourcesAddressLength = sizeof(SourcesAddress);
ReadMessageLength =
recvfrom(
ReadSockets.fd_array[i],
(LPSTR)RecvBuffer,
RecvBufferLength,
0,
(struct sockaddr *)&SourcesAddress,
&SourcesAddressLength );
if( ReadMessageLength == SOCKET_ERROR ) {
//
// when all sockets are closed, we are asked to return
// or something else has happpened which we can't
// handle.
//
Error = WSAGetLastError();
//
// free receive buffer.
//
SvclocHeap->Free( RecvBuffer );
RecvBuffer = NULL;
if( Error == WSAEMSGSIZE ) {
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"recvfrom() received a too large message (%ld).\n",
ReadMessageLength ));
continue; // process next message.
}
goto Cleanup;
}
TcpsvcsDbgAssert( ReadMessageLength <= RecvBufferLength );
//
// received a message.
//
TcpsvcsDbgPrint((
DEBUG_SVCLOC_MESSAGE,
"Received an query response message, %ld.\n",
ReadMessageLength ));
Error = ProcessSvclocQueryResponse(
ReadSockets.fd_array[i],
RecvBuffer,
(DWORD)ReadMessageLength,
(struct sockaddr *)&SourcesAddress,
(DWORD)SourcesAddressLength );
if( Error != ERROR_SUCCESS ) {
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"ProcessSvclocQueryResponse failed, %ld.\n", Error ));
//
// free receive buffer.
//
SvclocHeap->Free( RecvBuffer );
RecvBuffer = NULL;
}
}
}
//
// otherthan NT platform, receive NetBios responses also.
//
if( GlobalPlatformType != VER_PLATFORM_WIN32_NT ) {
LPSVCLOC_NETBIOS_RESPONSE NetBiosResponses = NULL;
DWORD NumResponses;
//
// compute remaining time.
//
if( pSockTimeout != NULL ) {
DWORD NewTickCountStart;
DWORD Elapse;
NewTickCountStart = GetTickCount();
//
// subtract elapse time.
//
Elapse = NewTickCountStart - TickCountStart;
if( Elapse > TimeoutinMSecs ) {
TimeoutinMSecs = 0;
}
else {
TimeoutinMSecs -= Elapse;
TickCountStart = NewTickCountStart;
}
}
//
// receive netbios responses.
//
Error = ReceiveNetBiosResponses(
&NetBiosResponses,
&NumResponses,
TimeoutinMSecs,
WaitForAllResponses );
if( Error != ERROR_SUCCESS ) {
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"ReceiveNetBiosResponses failed, %ld.\n", Error ));
//
// ignore this error and continue.
//
}
else {
LPSVCLOC_NETBIOS_RESPONSE NBResp = NetBiosResponses;
for( i = 0; i < NumResponses; i++, NBResp++ ) {
Error = ProcessSvclocQueryResponse(
0,
NBResp->ResponseBuffer,
NBResp->ResponseBufLen,
(struct sockaddr *)
&NBResp->SourcesAddress,
NBResp->SourcesAddrLen );
if( Error != ERROR_SUCCESS ) {
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"ProcessSvclocQueryResponse failed, %ld.\n",
Error ));
//
// free response receive buffer.
//
SvclocHeap->Free( NBResp->ResponseBuffer );
//
// ignore this error and continue.
//
}
}
//
// free up response buffer.
//
if( NetBiosResponses != NULL ) {
SvclocHeap->Free( NetBiosResponses );
}
}
}
else {
//
// if we have timed out.
//
if( NumReadySockets == 0 ) {
Error = ERROR_SUCCESS;
goto Cleanup;
}
}
if( WaitForAllResponses == FALSE ) {
//
// return after the first set of responses received.
//
Error = ERROR_SUCCESS;
break;
}
//
// compute remaining time.
//
if( pSockTimeout != NULL ) {
DWORD NewTickCountStart;
DWORD Elapse;
NewTickCountStart = GetTickCount();
//
// subtract elapse time.
//
Elapse = NewTickCountStart - TickCountStart;
if( Elapse > TimeoutinMSecs ) {
TimeoutinMSecs = 0;
Error = ERROR_SUCCESS;
goto Cleanup;
}
else {
TimeoutinMSecs -= Elapse;
TickCountStart = NewTickCountStart;
}
}
}
Cleanup:
//
// discovery is completed (successfully or not), indicate so.
//
LOCK_SVC_GLOBAL_DATA();
SetEvent( GlobalDiscoveryInProgressEvent );
GlobalLastDiscoveryTime = time( NULL );
UNLOCK_SVC_GLOBAL_DATA();
return( Error );
}
VOID
ServerDiscoverThread(
LPVOID Parameter
)
/*++
Routine Description:
This thread waits for query responses to arrive, when they arrive it
queues them in them in the global list.
Arguments:
Parameter - not used..
Return Value:
None.
--*/
{
DWORD Error;
Error = ReceiveResponses( RESPONSE_WAIT_TIMEOUT, TRUE );
if( Error != ERROR_SUCCESS ) {
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"ReceiveResponses returned error (%ld) .\n", Error ));
}
LOCK_SVC_GLOBAL_DATA();
//
// close thread handle.
//
CloseHandle( GlobalCliDiscoverThreadHandle );
GlobalCliDiscoverThreadHandle = NULL;
UNLOCK_SVC_GLOBAL_DATA();
return;
}
DWORD
MakeClientQueryMesage(
ULONGLONG ServicesMask
)
/*++
Routine Description:
This function makes client query message and stores it in the global
data buffer for future use.
The query message format.
1st DWORD : message length.
2nd DWORD : message version.
3rd ULONGLONG : services mask the client interested in.
4th WCHAR[] : client name
..
..
Last but one DWORD : check sum.
LAST DWORD : terminating DWORD 0xFFFFFFFF
Assume: Global data is locked.
Arguments:
ServicesMask - services to query for.
Return Value:
Windows Error Code.
--*/
{
LPCLIENT_QUERY_MESSAGE QueryMsg;
//
// Test to see query message is already made.
//
if( GlobalCliQueryMsg == NULL ) {
//
// check to computer name is initilaized.
//
if( GlobalComputerName[0] == '\0' ) {
DWORD ComputerNameLength =
MAX_COMPUTERNAME_LENGTH + 1;
//
// read computer name.
//
if( !GetComputerNameA(
GlobalComputerName,
&ComputerNameLength ) ) {
DWORD Error = GetLastError();
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"GetComputerNameA failed, %ld.\n", Error ));
return( Error );
}
GlobalComputerName[ComputerNameLength] = '\0';
}
//
// compute the space required for the query message.
//
DWORD MsgLen;
MsgLen = sizeof(CLIENT_QUERY_MESSAGE) +
strlen(GlobalComputerName) * sizeof(CHAR);
// note the terminating char is included as part of
// CLIENT_QUERY_MESSAGE size.
//
// Ceil to next DWORD.
//
MsgLen = ROUND_UP_COUNT(MsgLen, ALIGN_DWORD);
//
// add space for checksum and terminating DWORD.
//
MsgLen += sizeof(DWORD);
QueryMsg = (LPCLIENT_QUERY_MESSAGE) SvclocHeap->Alloc( MsgLen );
if( QueryMsg == NULL ) {
return( ERROR_NOT_ENOUGH_MEMORY );
}
memset( QueryMsg, 0x0, MsgLen );
//
// init fields.
//
INET_VERSION_NUM MessageVersion;
MessageVersion.Version.Major = INET_MAJOR_VERSION;
MessageVersion.Version.Minor = INET_MINOR_VERSION;
QueryMsg->MsgLength = MsgLen;
QueryMsg->MsgVersion = MessageVersion.VersionNumber;
QueryMsg->ServicesMask = ServicesMask;
strcpy( QueryMsg->ClientName, GlobalComputerName );
//
// compute check sum.
//
DWORD Checksum;
Checksum = ComputeCheckSum(
(LPBYTE)QueryMsg + sizeof(DWORD), // skip length field.
MsgLen - 3 * sizeof(DWORD) );
*(LPDWORD)((LPBYTE)QueryMsg + MsgLen - 2 * sizeof(DWORD)) = Checksum;
*(LPDWORD)((LPBYTE)QueryMsg + MsgLen - sizeof(DWORD)) = 0xFFFFFFFF;
GlobalCliQueryMsg = (LPBYTE)QueryMsg;
GlobalCliQueryMsgLen = MsgLen;
}
else {
//
// set the requested service mask in the message.
//
QueryMsg = (LPCLIENT_QUERY_MESSAGE)GlobalCliQueryMsg;
QueryMsg->ServicesMask |= ServicesMask;
}
return( ERROR_SUCCESS );
}
DWORD
CleanupOldResponses(
VOID
)
/*++
Routine Description:
This function removes and deletes the server responses that are older
than INET_SERVER_RESPONSE_TIMEOUT (15 mins).
ASSUME : the global lock is locked.
Arguments:
none.
Return Value:
Windows Error Code.
--*/
{
PLIST_ENTRY NextResponse;
LPCLIENT_QUERY_RESPONSE QueryResponse;
time_t CurrentTime;
CurrentTime = time( NULL );
NextResponse = GlobalCliQueryRespList.Flink;
while( NextResponse != &GlobalCliQueryRespList ) {
QueryResponse = (LPCLIENT_QUERY_RESPONSE)NextResponse;
NextResponse = NextResponse->Flink;
if( CurrentTime > QueryResponse->TimeStamp +
INET_SERVER_RESPONSE_TIMEOUT ) {
RemoveEntryList( (PLIST_ENTRY)QueryResponse );
//
// free up resources.
//
//
// free response buffer.
//
SvclocHeap->Free( QueryResponse->ResponseBuffer );
//
// free this node.
//
SvclocHeap->Free( QueryResponse );
}
}
return( ERROR_SUCCESS );
}
DWORD
GetDiscoveredServerInfo(
LPSTR ServerName,
IN ULONGLONG ServicesMask,
LPINET_SERVER_INFO *ServerInfo
)
/*++
Routine Description:
This function processes the responses received from the servers so far
and returns the response received from the specified server.
ASSUME : the global lock is locked.
Arguments:
ServerName : name of the server whose info to be queried.
ServicesMask : services to be queried
INetServerInfo : pointer to a location where the pointer to a
INET_SERVER_INFO structure is returned.
Return Value:
Windows Error Code.
--*/
{
DWORD Error;
PLIST_ENTRY NextResponse;
LPCLIENT_QUERY_RESPONSE QueryResponse;
LOCK_SVC_GLOBAL_DATA();
//
// first clean up timeout server records.
//
Error = CleanupOldResponses();
if( Error != ERROR_SUCCESS ) {
goto Cleanup;
}
//
// now loop through the list of responses and findout the response
// from the specified server.
//
for( NextResponse = GlobalCliQueryRespList.Flink;
NextResponse != &GlobalCliQueryRespList;
NextResponse = NextResponse->Flink ) {
QueryResponse = (LPCLIENT_QUERY_RESPONSE)NextResponse;
//
// check to see this entry is from the specified server.
//
if( _stricmp(
ServerName,
(LPSTR)QueryResponse->ResponseBuffer + 2 * sizeof(DWORD) )
// skip length and version DWORDS.
== 0 ) {
EMBED_SERVER_INFO *ServerInfoObj;
ServerInfoObj = new EMBED_SERVER_INFO(
QueryResponse->ResponseBuffer + sizeof(DWORD), // skip length field.
QueryResponse->ResponseBufferLength - 3 * sizeof(DWORD) );
// skip last 2 DWORDS too.
if( ServerInfoObj == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
Error = ServerInfoObj->GetStatus();
if( Error != ERROR_SUCCESS ) {
delete ServerInfoObj;
goto Cleanup;
}
//
// check services quaried.
//
if( ServicesMask & ServerInfoObj->GetServicesMask() ) {
//
// now make server info structure;
//
Error = ServerInfoObj->GetServerInfo( ServerInfo );
}
else {
//
// the server does not support the service(s) quaried.
//
Error = ERROR_BAD_NETPATH;
}
//
// delete the object which we don't require anymore.
//
delete ServerInfoObj;
//
// we are done.
//
goto Cleanup;
}
}
//
// unable to find the specified server.
//
Error = ERROR_BAD_NETPATH;
Cleanup:
UNLOCK_SVC_GLOBAL_DATA();
return( Error );
}
DWORD
ProcessDiscoveryResponses(
IN ULONGLONG ServicesMask,
OUT LPINET_SERVERS_LIST *INetServersList
)
/*++
Routine Description:
This function processes the responses received from the servers so far
and makes INET_SERVERS_LIST.
Arguments:
ServicesMask : services to be queried
INetServersList : pointer to a location where the pointer to a
INET_SERVERS_LIST structure is returned.
Return Value:
Windows Error Code.
--*/
{
DWORD Error;
PLIST_ENTRY NextResponse;
LPCLIENT_QUERY_RESPONSE QueryResponse;
DWORD NumServers;
LPINET_SERVERS_LIST ServersList;
LOCK_SVC_GLOBAL_DATA();
//
// first clean up timeout server records.
//
Error = CleanupOldResponses();
if( Error != ERROR_SUCCESS ) {
goto Cleanup;
}
//
// compute number of responses we have.
//
NumServers = 0;
for( NextResponse = GlobalCliQueryRespList.Flink;
NextResponse != &GlobalCliQueryRespList;
NextResponse = NextResponse->Flink ) {
NumServers++;
}
//
// allocate memory for servers list structure.
//
ServersList = (LPINET_SERVERS_LIST)
SvclocHeap->Alloc( sizeof(INET_SERVERS_LIST) );
if( ServersList == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
ServersList->NumServers = 0;
ServersList->Servers = NULL;
//
// now allocate memory for the servers info structure pointers array.
//
ServersList->Servers = (LPINET_SERVER_INFO *)
SvclocHeap->Alloc(NumServers * sizeof(LPINET_SERVER_INFO) );
if( ServersList->Servers == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// loop through the list of responses and make response structures.
//
for( NextResponse = GlobalCliQueryRespList.Flink;
NextResponse != &GlobalCliQueryRespList;
NextResponse = NextResponse->Flink ) {
EMBED_SERVER_INFO *ServerInfoObj;
QueryResponse = (LPCLIENT_QUERY_RESPONSE)NextResponse;
ServerInfoObj = new EMBED_SERVER_INFO(
QueryResponse->ResponseBuffer + sizeof(DWORD), // skip length field.
QueryResponse->ResponseBufferLength - 3 * sizeof(DWORD) );
// skip last 2 DWORDS too.
if( ServerInfoObj == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
Error = ServerInfoObj->GetStatus();
if( Error != ERROR_SUCCESS ) {
delete ServerInfoObj;
goto Cleanup;
}
//
// check services quaried.
//
if( ServicesMask & ServerInfoObj->GetServicesMask() ) {
//
// the server has the one or more of the services quaried.
// add an entry in the return buffer.
//
DWORD i;
i = ServersList->NumServers;
//
// now make server info structure;
//
Error = ServerInfoObj->GetServerInfo( &ServersList->Servers[i] );
if( Error != ERROR_SUCCESS ) {
TcpsvcsDbgAssert( ServersList->Servers[i] == NULL );
delete ServerInfoObj;
ServerInfoObj = NULL;
goto Cleanup;
}
//
// allocate space for server address.
//
LPVOID ServerAddress;
ServerAddress = SvclocHeap->Alloc( QueryResponse->SourcesAddressLength );
if( ServerAddress != NULL ) {
//
// copy server address.
//
TcpsvcsDbgAssert( ServersList->Servers[i]->ServerAddress.BindData == NULL );
TcpsvcsDbgAssert( ServersList->Servers[i]->ServerAddress.Length == 0 );
memcpy(
ServerAddress,
QueryResponse->SourcesAddress,
QueryResponse->SourcesAddressLength );
ServersList->Servers[i]->ServerAddress.BindData = ServerAddress;
ServersList->Servers[i]->ServerAddress.Length =
QueryResponse->SourcesAddressLength;
}
//
// we success fully added another server info, indicate so.
//
ServersList->NumServers++;
}
//
// delete the object which we don't require anymore.
//
delete ServerInfoObj;
ServerInfoObj = NULL;
}
//
// now set up return pointer.
//
*INetServersList = ServersList;
ServersList = NULL;
Error = ERROR_SUCCESS;
Cleanup:
UNLOCK_SVC_GLOBAL_DATA();
if( ServersList != NULL ) {
//
// free server info structures first.
//
if( ServersList->NumServers > 0 ) {
TcpsvcsDbgAssert( ServersList->Servers != NULL );
}
DWORD Index;
for (Index = 0; Index < ServersList->NumServers; Index++) {
TcpsvcsDbgAssert( ServersList->Servers[Index] != NULL );
FreeServerInfo( ServersList->Servers[Index] );
}
if( ServersList->Servers != NULL ) {
SvclocHeap->Free( ServersList->Servers );
}
SvclocHeap->Free( ServersList );
}
if( Error != ERROR_SUCCESS ) {
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"ProcessDiscoveryResponses returning error, %ld.", Error ));
}
return( Error );
}
DWORD
ProcessNcbResponse(
NCB *Ncb,
PLIST_ENTRY RespList,
DWORD *NumEntries
)
{
LPBYTE RespBuffer;
DWORD RespBufferLen;
if ( Ncb->ncb_retcode == NRC_GOODRET ) {
// DebugBreak();
//
// copy response buffer pointer.
//
RespBuffer = Ncb->ncb_buffer;
RespBufferLen = Ncb->ncb_length;
//
// allocate a new buffer.
//
LPBYTE RecvBuffer;
RecvBuffer = (LPBYTE )
SvclocHeap->Alloc( SVCLOC_CLI_QUERY_RESP_BUF_SIZE );
if( RecvBuffer == NULL ) {
return( ERROR_NOT_ENOUGH_MEMORY );
}
Ncb->ncb_buffer = RecvBuffer;
Ncb->ncb_length = SVCLOC_CLI_QUERY_RESP_BUF_SIZE;
//
// resubmit the NCB with different buffer.
//
UCHAR NBErrorCode;
NBErrorCode = Netbios( Ncb );
if( (NBErrorCode != NRC_GOODRET) ||
(Ncb->ncb_retcode != NRC_PENDING) ) {
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"Netbios() failed, %ld, %ld \n",
NBErrorCode, Ncb->ncb_retcode ));
return( ERROR_BAD_NETPATH );
}
}
else {
TcpsvcsDbgPrint(( DEBUG_ERRORS,
"Netbios() failed, %ld\n", Ncb->ncb_retcode ));
return( ERROR_BAD_NETPATH ); // ??
}
//
// create a response entry and add to the list.
//
LPSVCLOC_NETBIOS_RESP_ENTRY RespEntry;
RespEntry = (LPSVCLOC_NETBIOS_RESP_ENTRY)
SvclocHeap->Alloc( sizeof(SVCLOC_NETBIOS_RESP_ENTRY) );
if( RespEntry == NULL ) {
return( ERROR_NOT_ENOUGH_MEMORY );;
}
RespEntry->Resp.ResponseBuffer = RespBuffer;
RespEntry->Resp.ResponseBufLen = RespBufferLen;
RespEntry->Resp.SourcesAddress.snb_family = AF_NETBIOS;
RespEntry->Resp.SourcesAddress.snb_type =
TDI_ADDRESS_NETBIOS_TYPE_UNIQUE;
memcpy(
RespEntry->Resp.SourcesAddress.snb_name,
Ncb->ncb_callname,
sizeof(Ncb->ncb_callname) );
RespEntry->Resp.SourcesAddrLen = sizeof(SOCKADDR_NB);
(*NumEntries)++;
InsertTailList( RespList, (PLIST_ENTRY)RespEntry );
return( ERROR_SUCCESS );
}
VOID
NcbPostHandler(
NCB *Ncb
)
{
ProcessNcbResponse(
Ncb,
&GlobalWin31NBRespList,
&GlobalWin31NumNBResps
);
return;
}
DWORD
DiscoverNetBiosServers(
LPSTR ServerName
)
/*++
Routine Description:
This function sends out discovery message over netbios NBCs
Arguments:
ServerName : name of the specific server to discover.
Return Value:
Windows Error Code.
--*/
{
NCB *Ncbs = NULL;
LANA_ENUM Lanas;
DWORD Error = ERROR_SUCCESS;
UCHAR NBErrorCode = NRC_GOODRET;
UCHAR UniqueName[NCBNAMSZ];
// DebugBreak();
LOCK_SVC_GLOBAL_DATA();
if( !GetNetBiosLana( &Lanas ) ) {
return( ERROR_BAD_NETPATH );
}
TcpsvcsDbgAssert( Lanas.length != 0 );
if( Lanas.length == 0 ) {
return( ERROR_BAD_NETPATH );
}
Ncbs = (NCB *) SvclocHeap->Alloc( sizeof(NCB ) * Lanas.length );
if( Ncbs == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
memset( Ncbs, 0x0, sizeof(NCB) * Lanas.length );
GetNBUniqueName( (LPSTR)UniqueName, NCBNAMSZ );
//
// alloc memory for the pending recvs.
//
#define NUM_RECV_PENDING_NCBS_PER_LANA 3
GlobalNumNBPendingRecvs = 0;
GlobalNBPendingRecvs = (NCB *)
SvclocHeap->Alloc(
sizeof(NCB ) *
Lanas.length *
NUM_RECV_PENDING_NCBS_PER_LANA );
if( GlobalNBPendingRecvs == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
DWORD i;
for( i = 0; i < Lanas.length; i++ ) {
NCB *PendingRecv;
LPBYTE RecvBuffer;
HANDLE EventHandle;
Ncbs[i].ncb_command = NCBADDNAME;
memcpy( Ncbs[i].ncb_name, UniqueName, NCBNAMSZ );
//
// add name.
//
Ncbs[i].ncb_lana_num = Lanas.lana[i];
// DebugBreak();
NBErrorCode = Netbios( &Ncbs[i] );
if( Ncbs[i].ncb_retcode != NRC_GOODRET ) {
NBErrorCode = Ncbs[i].ncb_retcode;
}
if( NBErrorCode != NRC_GOODRET ) {
goto Cleanup;
}
//
// post pending receives.
//
DWORD j;
for( j = 0; j < NUM_RECV_PENDING_NCBS_PER_LANA; j++ ) {
TcpsvcsDbgAssert(
GlobalNumNBPendingRecvs <
( (DWORD)Lanas.length *
NUM_RECV_PENDING_NCBS_PER_LANA) );
PendingRecv =
&GlobalNBPendingRecvs[GlobalNumNBPendingRecvs];
memset( PendingRecv, 0x0, sizeof(NCB) );
memcpy(
PendingRecv->ncb_name,
Ncbs[i].ncb_name,
NCBNAMSZ );
PendingRecv->ncb_lana_num = Lanas.lana[i];
PendingRecv->ncb_command = NCBDGRECV | ASYNCH;
PendingRecv->ncb_rto = SVCLOC_NB_RECV_TIMEOUT / 2;
PendingRecv->ncb_num = Ncbs[i].ncb_num;
RecvBuffer = (LPBYTE)
SvclocHeap->Alloc( SVCLOC_CLI_QUERY_RESP_BUF_SIZE );
if( RecvBuffer == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
PendingRecv->ncb_buffer = RecvBuffer;
PendingRecv->ncb_length = SVCLOC_CLI_QUERY_RESP_BUF_SIZE;
if( GlobalPlatformType == VER_PLATFORM_WIN32s ) {
PendingRecv->ncb_post = NcbPostHandler ;
}
else {
//
// create an event.
//
EventHandle = IIS_CREATE_EVENT(
"NCB::ncb_event",
PendingRecv,
FALSE, // automatic reset
FALSE // initial state: NOT signalled
);
if( EventHandle == NULL ) {
Error = GetLastError();
SvclocHeap->Free( RecvBuffer );
goto Cleanup;
}
PendingRecv->ncb_event = EventHandle;
}
// DebugBreak();
NBErrorCode = Netbios( PendingRecv );
if( (PendingRecv->ncb_retcode != NRC_PENDING) &&
(PendingRecv->ncb_retcode != NRC_GOODRET) ) {
NBErrorCode = PendingRecv->ncb_retcode;
}
if( NBErrorCode != NRC_GOODRET ) {
SvclocHeap->Free( RecvBuffer );
CloseHandle( EventHandle );
goto Cleanup;
}
GlobalNumNBPendingRecvs++;
}
}
//
// send discovery message to all lanas.
//
for( i = 0; i < Lanas.length; i++ ) {
Ncbs[i].ncb_command = NCBDGSEND;
Ncbs[i].ncb_lana_num = Lanas.lana[i];
Ncbs[i].ncb_retcode = NRC_GOODRET;
//
// setup sender's name.
//
if( ServerName == NULL ) {
//
// if no server name is specified, then send the discovery message
// to IC group name.
//
TcpsvcsDbgAssert(
NETBIOS_INET_GROUP_NAME_LEN ==
NCBNAMSZ );
memcpy(
Ncbs[i].ncb_callname,
NETBIOS_INET_GROUP_NAME,
NETBIOS_INET_GROUP_NAME_LEN );
}
else {
DWORD ServerNameLen;
//
// send the discovery message to the specified server.
//
ServerNameLen = strlen(ServerName);
TcpsvcsDbgAssert( ServerNameLen <= NCBNAMSZ );
memset(
Ncbs[i].ncb_callname,
0x0,
NCBNAMSZ );
memcpy(
Ncbs[i].ncb_callname,
ServerName,
(ServerNameLen >= NCBNAMSZ) ? NCBNAMSZ : ServerNameLen );
}
//
// setup message buffer.
//
Ncbs[i].ncb_buffer = GlobalCliQueryMsg;
Ncbs[i].ncb_length = (WORD)GlobalCliQueryMsgLen;
// DebugBreak();
NBErrorCode = Netbios( &Ncbs[i] );
if( Ncbs[i].ncb_retcode != NRC_GOODRET ) {
NBErrorCode = Ncbs[i].ncb_retcode;
}
if( NBErrorCode != NRC_GOODRET ) {
// DebugBreak();
goto Cleanup;
}
}
Cleanup:
if( (Error != ERROR_SUCCESS) ||
(NBErrorCode != NRC_GOODRET) ) {
for( i = 0; i < GlobalNumNBPendingRecvs; i++ ) {
NCB Ncb;
NCB *PendingEntry;
BOOL CancelNcb;
memset( &Ncb, 0x0, sizeof(NCB) );
//
// cancel pending receives and free resources.
//
Ncb.ncb_command = NCBCANCEL;
Ncb.ncb_length = sizeof( NCB );
PendingEntry = &GlobalNBPendingRecvs[i];
CancelNcb = FALSE;
if( GlobalPlatformType == VER_PLATFORM_WIN32s ) {
if( PendingEntry->ncb_retcode == NRC_PENDING ) {
CancelNcb = TRUE;
}
}
else {
//
// check to see the event is signalled.
//
DWORD Wait;
Wait = WaitForSingleObject(
PendingEntry->ncb_event,
0 );
if( Wait == WAIT_TIMEOUT ) {
CancelNcb = TRUE;
}
}
if( CancelNcb == TRUE ) {
UCHAR NcbError;
Ncb.ncb_retcode = NRC_GOODRET;
Ncb.ncb_buffer = (LPBYTE)PendingEntry;
Ncb.ncb_lana_num = PendingEntry->ncb_lana_num;
// DebugBreak();
NcbError = Netbios( &Ncb );
if( (NcbError != NRC_GOODRET) ||
(Ncb.ncb_retcode != NRC_GOODRET) ) {
TcpsvcsDbgPrint((
DEBUG_ERRORS,
"Netbios() failed, %ld, %ld \n",
(DWORD)NcbError,
(DWORD)Ncb.ncb_retcode ));
}
}
//
// free receive buffer.
//
SvclocHeap->Free( PendingEntry->ncb_buffer );
CloseHandle( PendingEntry->ncb_event );
}
if( GlobalNBPendingRecvs != NULL ) {
SvclocHeap->Free( GlobalNBPendingRecvs );
GlobalNBPendingRecvs = NULL;
}
GlobalNumNBPendingRecvs = 0;
UNLOCK_SVC_GLOBAL_DATA();
TcpsvcsDbgPrint((
DEBUG_ERRORS,
"DiscoveryNetBiosServers failed,"
"NBErrorCode = %ld, Error = %ld \n",
(DWORD)NBErrorCode, Error ));
return( ERROR_BAD_NETPATH );
}
if( Ncbs != NULL ) {
SvclocHeap->Free(Ncbs);
}
UNLOCK_SVC_GLOBAL_DATA();
return( ERROR_SUCCESS );
}
DWORD
ReceiveNetBiosResponses(
LPSVCLOC_NETBIOS_RESPONSE *NetBiosResponses,
DWORD *NumResponses,
DWORD TimeoutinMSecs,
BOOL WaitForAllResponses
)
/*++
Routine Description:
This function collects all responses that are received for the NetBios
discovery.
Arguments:
NetBiosResponses : pointer to a location where the responses buffer
is returned.
NumResponses : pointer to a location where the number of responses in
the above buffer is returned
TimeoutinMSecs : wait timeout for responses.
WaitForAllResponses : If this flag is set TRUE, this function wait
complete 'Time' secs for all responses to arrive. Otherwise it
will return after a succcessful response is received.
Return Value:
Windows Error Code.
--*/
{
DWORD Error;
DWORD i;
NCB *PendingRecvs;
DWORD NumPendingRecvs;
HANDLE *WaitHandles = NULL;
LIST_ENTRY RespListHead;
PLIST_ENTRY RespList;
DWORD NumResps;
// DebugBreak();
*NumResponses = 0;
//
// init.
//
InitializeListHead( &RespListHead );
RespList = &RespListHead;
NumResps = 0;
//
// copy receive list.
//
LOCK_SVC_GLOBAL_DATA();
PendingRecvs = GlobalNBPendingRecvs;
NumPendingRecvs = GlobalNumNBPendingRecvs;
GlobalNBPendingRecvs = NULL;
GlobalNumNBPendingRecvs = 0;
UNLOCK_SVC_GLOBAL_DATA();
if( NumPendingRecvs == 0 ) {
//
// we are done.
//
Error = ERROR_SUCCESS;
goto Cleanup;
}
if( GlobalPlatformType == VER_PLATFORM_WIN32s ) {
//
// wait for responses to arrive.
//
Sleep( TimeoutinMSecs );
//
// get the list of responses that have been gathered by the
// handler routine.
//
RespList = &GlobalWin31NBRespList;
NumResps = GlobalWin31NumNBResps;
}
else {
WaitHandles = (HANDLE *)
SvclocHeap->Alloc( NumPendingRecvs * sizeof(HANDLE) );
if( WaitHandles == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
HANDLE *WaitHandleEntry;
WaitHandleEntry = WaitHandles;
for( i = 0; i < NumPendingRecvs; i++ ) {
*WaitHandleEntry = PendingRecvs[i].ncb_event;
WaitHandleEntry++;
}
//
// wait for all pending receives.
//
DWORD StartTime;
StartTime = GetTickCount();
for( ;; ) {
DWORD Wait;
NCB *SignalledNcb;
Wait = WaitForMultipleObjects(
NumPendingRecvs,
WaitHandles,
FALSE, // wait for one.
TimeoutinMSecs );
if( Wait == WAIT_FAILED ) {
Error = GetLastError();
goto Cleanup;
}
if( Wait == WAIT_TIMEOUT ) {
break;
}
//
// one of the handle has been signalled.
//
Wait -= WAIT_OBJECT_0; // index to the handle.
TcpsvcsDbgAssert( Wait < NumPendingRecvs );
SignalledNcb = &PendingRecvs[Wait];
Error = ProcessNcbResponse(
&PendingRecvs[Wait],
RespList,
&NumResps );
TcpsvcsDbgAssert( Error == ERROR_SUCCESS );
if( Error != ERROR_SUCCESS ) {
goto Cleanup;
}
//
// recompute wait time.
//
DWORD EndTime;
DWORD Elapse;
EndTime = GetTickCount();
Elapse = EndTime - StartTime;
//
// set TIMEOUT to zero if we are asked to return after a first
// set of responses received or if the given time elapses.
//
if( (WaitForAllResponses == FALSE) ||
(Elapse > TimeoutinMSecs) ) {
TimeoutinMSecs = 0;
}
else {
TimeoutinMSecs -= Elapse;
}
StartTime = EndTime;
}
}
if( NumResps == 0 ) {
//
// we are done.
//
Error = ERROR_SUCCESS;
goto Cleanup;
}
//
// allocate space for return structures.
//
LPSVCLOC_NETBIOS_RESPONSE RetResps;
RetResps = (LPSVCLOC_NETBIOS_RESPONSE)
SvclocHeap->Alloc(
NumResps * sizeof(SVCLOC_NETBIOS_RESPONSE) );
if( RetResps == NULL ) {
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// copy entries from list to return array.
//
DWORD NumRetEntries;
PLIST_ENTRY RetEntry;
NumRetEntries = 0;
for( RetEntry = RespList->Flink;
RetEntry != RespList;
RetEntry = RetEntry->Flink ) {
LPSVCLOC_NETBIOS_RESP_ENTRY REntry;
REntry = (LPSVCLOC_NETBIOS_RESP_ENTRY)RetEntry;
RetResps[NumRetEntries] = REntry->Resp;
//
// don't free up the returned response buffer.
//
REntry->Resp.ResponseBuffer = NULL;
NumRetEntries++;
}
TcpsvcsDbgAssert( NumRetEntries == NumResps );
*NumResponses = NumRetEntries;
*NetBiosResponses = RetResps;
Error = ERROR_SUCCESS;
Cleanup:
if( WaitHandles != NULL ) {
SvclocHeap->Free( WaitHandles );
}
//
// free response list.
//
while ( !IsListEmpty(RespList) ) {
PLIST_ENTRY Entry;
Entry = RemoveHeadList( RespList );
//
// free response buffer if it is not used.
//
if( ((LPSVCLOC_NETBIOS_RESP_ENTRY)
Entry)->Resp.ResponseBuffer != NULL ) {
SvclocHeap->Free(
((LPSVCLOC_NETBIOS_RESP_ENTRY)
Entry)->Resp.ResponseBuffer );
}
SvclocHeap->Free( Entry );
}
//
// cancel all pending recvs and delete names.
//
if( NumPendingRecvs != 0 ) {
NCB Ncb;
memset( &Ncb, 0x0, sizeof(NCB) );
Ncb.ncb_command = NCBCANCEL;
Ncb.ncb_length = sizeof( NCB );
for( i = 0; i < NumPendingRecvs; i++ ) {
NCB *PendingEntry;
UCHAR NcbError;
DWORD Wait;
BOOL CancelNcb;
PendingEntry = &PendingRecvs[i];
CancelNcb = FALSE;
if( GlobalPlatformType == VER_PLATFORM_WIN32s ) {
if( PendingEntry->ncb_retcode == NRC_PENDING ) {
CancelNcb = TRUE;
}
}
else {
//
// check to see the event is signalled.
//
DWORD Wait;
Wait = WaitForSingleObject(
PendingEntry->ncb_event,
0 );
if( Wait == WAIT_TIMEOUT ) {
CancelNcb = TRUE;
}
}
if( CancelNcb == TRUE ) {
Ncb.ncb_retcode = NRC_GOODRET;
Ncb.ncb_buffer = (LPBYTE)PendingEntry;
Ncb.ncb_length = sizeof( NCB );
Ncb.ncb_lana_num = PendingEntry->ncb_lana_num;
Ncb.ncb_command = NCBCANCEL;
NcbError = Netbios( &Ncb );
if( (NcbError != NRC_GOODRET) ||
(Ncb.ncb_retcode != NRC_GOODRET) ) {
TcpsvcsDbgPrint((
DEBUG_ERRORS,
"Netbios() failed, %ld, %ld \n",
(DWORD)NcbError,
(DWORD)Ncb.ncb_retcode ));
}
}
//
// delete name.
//
Ncb.ncb_retcode = NRC_GOODRET;
memcpy(
Ncb.ncb_name,
PendingEntry->ncb_name,
sizeof( Ncb.ncb_name ) );
Ncb.ncb_lana_num = PendingEntry->ncb_lana_num;
Ncb.ncb_command = NCBDELNAME;
NcbError = Netbios( &Ncb );
if( (NcbError != NRC_GOODRET) ||
(Ncb.ncb_retcode != NRC_GOODRET) ) {
TcpsvcsDbgPrint((
DEBUG_ERRORS,
"Netbios() failed, %ld, %ld \n",
(DWORD)NcbError,
(DWORD)Ncb.ncb_retcode ));
}
//
// free receive buffer.
//
SvclocHeap->Free( PendingEntry->ncb_buffer );
CloseHandle( PendingEntry->ncb_event );
}
SvclocHeap->Free( PendingRecvs );
}
return( Error );
}