|
|
/*++
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: Sean Woodward (t-seanwo) 26-October-1997 ADSI Update
--*/
#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, ' ', (NameEndPtr - NamePtr) ); } else {
memcpy( NamePtr, GlobalComputerName, (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 ); }
|