mirror of https://github.com/tongzx/nt5src
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.
2979 lines
68 KiB
2979 lines
68 KiB
/*++
|
|
|
|
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 );
|
|
}
|
|
|
|
|
|
|