/*++ Copyright (c) 1994 Microsoft Corporation Module Name: svccom.cxx Abstract: Contains code that is common to both client and server side of the service location protocol.. Author: Madan Appiah (madana) 15-May-1995 Environment: User Mode - Win32 Revision History: --*/ #include DWORD g_cInitializers = 0; // // include global.h one more time to alloc global data. // // // to enable second time include. // #undef _GLOBAL_ #undef EXTERN // // to allocate data // #define GLOBAL_SVC_DATA_ALLOCATE #include DWORD MakeSapServiceName( LPSTR SapNameBuffer, DWORD SapNameBufferLen ) /*++ Routine Description: This routine generates a sap service name. The first part of the name is computername and last part is the string version of service guid. Arguments: SapNameBuffer - pointer to a sap name buffer where sap name is returned. SapNameBufferLen - length of the above buffer. Return Value: pointer to sap service name.. --*/ { TcpsvcsDbgAssert( SapNameBufferLen >= (SAP_SERVICE_NAME_LEN + 1)); if( SapNameBufferLen < SAP_SERVICE_NAME_LEN + 1) { return( ERROR_INSUFFICIENT_BUFFER ); } // // Get Computername. // DWORD Len = SapNameBufferLen; if( !GetComputerNameA( SapNameBuffer, &Len ) ) { DWORD Error = GetLastError(); TcpsvcsDbgPrint(( DEBUG_ERRORS, "GetComputerNameA failed, %ld.\n", Error )); return( Error ); } TcpsvcsDbgAssert( Len <= MAX_COMPUTERNAME_LENGTH ); while( Len < MAX_COMPUTERNAME_LENGTH ) { SapNameBuffer[Len++] = '!'; } // // append GUID. // strcpy( SapNameBuffer + Len, SERVICE_GUID_STR ); return( ERROR_SUCCESS ); } VOID MakeUniqueServerName( LPBYTE StrBuffer, DWORD StrBufferLen, LPSTR ComputerName ) /*++ Routine Description: This routine makes an unique name used by the server to listen to the client discovery requests. Arguments: StrBuffer : pointer to a buffer where the unique name is returned. StrBufferLen : length of the above buffer. ComputerName : pointer to the computer that is used to form the unique name. Return Value: none. --*/ { DWORD ComputerNameLen = strlen(ComputerName); DWORD BufLen = StrBufferLen; LPBYTE Buffer = StrBuffer; memset( Buffer, 0x0, BufLen ); memcpy( Buffer, NETBIOS_INET_SERVER_UNIQUE_NAME, NETBIOS_INET_SERVER_UNIQUE_NAME_LEN ); BufLen -= NETBIOS_INET_SERVER_UNIQUE_NAME_LEN; Buffer += NETBIOS_INET_SERVER_UNIQUE_NAME_LEN; if( BufLen >= ComputerNameLen ) { // // we enough space in the buffer to append computername. // memcpy( Buffer,ComputerName, ComputerNameLen ); return; } // // buffer does not have enough space, chop off few chars from the // begining of the computername. // memcpy( Buffer, ComputerName + (ComputerNameLen - BufLen), BufLen ); return; } #if 0 VOID AppendRandomChar( LPSTR String ) /*++ Routine Description: This routine adds a random char to the end of the given string. It is assumed that the given string has a space for the new char. Arguments: String : pointer to a string where a random char is added. Return Value: none. --*/ { CHAR RandChar; DWORD RandNum; RandNum = (DWORD)rand(); RandNum = RandNum % (26 + 10); // 26 alphabets, and 10 numerics if( RandNum < 10 ) { RandChar = (CHAR)('0'+ RandNum); } else { RandChar = (CHAR)('A'+ RandNum - 10); } DWORD Len = strlen(String); // // append random char. // String[Len] = RandChar; String[Len + 1] = '\0'; return; } #endif //0 DWORD ComputeCheckSum( LPBYTE Buffer, DWORD BufferLength ) /*++ Routine Description: This function computes the check sum of the given buffer by ex-or'ing the dwords. It is assumed that the buffer DWORD aligned and the buffer length is multiples of DWORD. Arguments: Buffer : pointer to a buffer whose check sum to be computed. BufferLength : length of the above buffer. Return Value: Check sum. --*/ { DWORD CheckSum = 0; LPDWORD BufferPtr = (LPDWORD)Buffer; LPBYTE EndBuffer = Buffer + BufferLength; TcpsvcsDbgAssert( (ULONG_PTR)Buffer % sizeof(DWORD) == 0 ); // alignment check. TcpsvcsDbgAssert( BufferLength % sizeof(DWORD) == 0 ); // multiple DWORD check. while( (LPBYTE)BufferPtr < EndBuffer ) { CheckSum ^= *BufferPtr; BufferPtr++; } return( CheckSum ); } BOOL DLLSvclocEntry( IN HINSTANCE DllHandle, IN DWORD Reason, IN LPVOID Reserved ) /*++ Routine Description: Performs global initialization and termination for all protocol modules. This function only handles process attach and detach which are required for global initialization and termination, respectively. We disable thread attach and detach. New threads calling Wininet APIs will get an INTERNET_THREAD_INFO structure created for them by the first API requiring this structure Arguments: DllHandle - handle of this DLL. Unused Reason - process attach/detach or thread attach/detach Reserved - if DLL_PROCESS_ATTACH, NULL means DLL is being dynamically loaded, else static. For DLL_PROCESS_DETACH, NULL means DLL is being freed as a consequence of call to FreeLibrary() else the DLL is being freed as part of process termination Return Value: BOOL Success - TRUE Failure - FALSE. Failed to initialize --*/ { BOOL ok; DWORD error; UNREFERENCED_PARAMETER(DllHandle); // // perform global dll initialization, if any. // switch (Reason) { case DLL_PROCESS_ATTACH: // // we switch off thread library calls to avoid taking a hit for every // thread creation/termination that happens in this process, regardless // of whether Internet APIs are called in the thread. // // If a new thread does make Internet API calls that require a per-thread // structure then the individual API will create one // DisableThreadLibraryCalls(DllHandle); // // Old Normandy servers that are in our process are assuming the // service locator is initialized by DLL_PROCESS_ATTACH and terminated // by PROCESS_DETACH. Since the service locator has a thread we // can't safely cleanup during process_detach so we do an extra // loadlibrary on ourselves and remain in process. When the Normandy // servers are updated, remove this. // if ( !InitSvcLocator() || !LoadLibrary( "inetsloc.dll" )) { return FALSE; } break; case DLL_PROCESS_DETACH: break; } return (TRUE); } BOOL InitSvcLocator( VOID ) { // // We assume the caller is serializing access from multiple initializers. // The callers will presumably be just infocomm.dll that does do the // serialization. // // if ( g_cInitializers++ ) { return TRUE; } if ( DllProcessAttachSvcloc() != ERROR_SUCCESS ) { return FALSE; } return TRUE; } BOOL TerminateSvcLocator( VOID ) { if ( --g_cInitializers ) return TRUE; DllProcessDetachSvcloc(); return TRUE; } DWORD DllProcessAttachSvcloc( VOID ) /*++ Routine Description: This dll init function initializes service location global variables. Arguments: NONE. Return Value: Windows Error Code. --*/ { DWORD Error; // // initialize global variables. // // DebugBreak(); #if DBG // // initialize dbg crit sect. // INITIALIZE_CRITICAL_SECTION( &GlobalDebugCritSect ); GlobalDebugFlag = DEBUG_ERRORS; #endif // DBG INITIALIZE_CRITICAL_SECTION( &GlobalSvclocCritSect ); LOCK_SVC_GLOBAL_DATA(); GlobalComputerName[0] = '\0'; GlobalSrvRegistered = FALSE; SvclocHeap = new MEMORY; if( SvclocHeap == NULL ) { UNLOCK_SVC_GLOBAL_DATA(); return( ERROR_NOT_ENOUGH_MEMORY ); } GlobalSrvInfoObj = NULL; GlobalSrvRespMsg = NULL; GlobalSrvRespMsgLength = 0; GlobalSrvAllotedRespMsgLen = 0; GlobalWinsockStarted = FALSE; GlobalRNRRegistered = FALSE; GlobalSrvListenThreadHandle = NULL; memset( &GlobalSrvSockets, 0x0, sizeof(GlobalSrvSockets) ); GlobalCliDiscoverThreadHandle = NULL; GlobalCliQueryMsg = NULL; GlobalCliQueryMsgLen = 0; GlobalSapGuid.Data1 = ssgData1; GlobalSapGuid.Data2 = ssgData2; GlobalSapGuid.Data3 = ssgData3; GlobalSapGuid.Data4[0] = ssgData41; GlobalSapGuid.Data4[1] = ssgData42; GlobalSapGuid.Data4[2] = ssgData43; GlobalSapGuid.Data4[3] = ssgData44; GlobalSapGuid.Data4[4] = ssgData45; GlobalSapGuid.Data4[5] = ssgData46; GlobalSapGuid.Data4[6] = ssgData47; GlobalSapGuid.Data4[7] = ssgData48; InitializeListHead( &GlobalCliQueryRespList ); memset( &GlobalCliSockets, 0x0, sizeof(GlobalCliSockets) ); memset( &GlobalCliNBSockets, 0x0, sizeof(GlobalCliNBSockets) ); GlobalCliIpxSocket = INVALID_SOCKET; GlobalDiscoveryInProgressEvent = IIS_CREATE_EVENT( "GlobalDiscoveryInProgressEvent", &GlobalDiscoveryInProgressEvent, TRUE, // MANUAL reset TRUE // initial state: signalled ); if ( GlobalDiscoveryInProgressEvent == NULL ) { Error = GetLastError(); UNLOCK_SVC_GLOBAL_DATA(); return(Error); } GlobalLastDiscoveryTime = 0; // // get platform type. // OSVERSIONINFO VersionInfo; VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo); if ( (GetVersionEx(&VersionInfo)) ) { GlobalPlatformType = VersionInfo.dwPlatformId; } else { UNLOCK_SVC_GLOBAL_DATA(); return( ERROR_INSUFFICIENT_BUFFER ); } GlobalNumNBPendingRecvs = 0; GlobalNBPendingRecvs = NULL; InitializeListHead( &GlobalWin31NBRespList ); GlobalWin31NumNBResps = 0; UNLOCK_SVC_GLOBAL_DATA(); srand( (unsigned)time(NULL)); return( ERROR_SUCCESS ); } DWORD DllProcessDetachSvcloc( VOID ) /*++ Routine Description: This fundtion frees the global service location objects. Arguments: NONE. Return Value: Windows Error Code. --*/ { LOCK_SVC_GLOBAL_DATA(); if( GlobalSrvRegistered ) { ServerDeregisterAndStopListen(); } if( GlobalSrvInfoObj != NULL ) { delete GlobalSrvInfoObj; GlobalSrvInfoObj = NULL; } if( GlobalSrvRespMsg != NULL ) { SvclocHeap->Free( GlobalSrvRespMsg ); GlobalSrvRespMsg = NULL; GlobalSrvRespMsgLength = 0; GlobalSrvAllotedRespMsgLen = 0; } if( GlobalSrvRecvBuf != NULL ) { SvclocHeap->Free( GlobalSrvRecvBuf ); GlobalSrvRecvBuf = NULL; GlobalSrvRecvBufLength = 0; } if( GlobalCliQueryMsg != NULL ) { SvclocHeap->Free( GlobalCliQueryMsg ); GlobalCliQueryMsg = NULL; GlobalCliQueryMsgLen = 0; } InitializeListHead( &GlobalCliQueryRespList ); while( !IsListEmpty( &GlobalCliQueryRespList ) ) { LPCLIENT_QUERY_RESPONSE QueryResponse; // // remove head entry and free it up. // QueryResponse = (LPCLIENT_QUERY_RESPONSE) RemoveHeadList( &GlobalCliQueryRespList ); // // free response buffer. // SvclocHeap->Free( QueryResponse->ResponseBuffer ); // // free this node. // SvclocHeap->Free( QueryResponse ); } // // close client sockets. // DWORD i; for( i = 0; i < GlobalCliSockets.fd_count; i++ ) { closesocket( GlobalCliSockets.fd_array[i] ); } // // invalidate client handles. // memset( &GlobalCliSockets, 0x0, sizeof(GlobalCliSockets) ); memset( &GlobalCliNBSockets, 0x0, sizeof(GlobalCliNBSockets) ); GlobalCliIpxSocket = INVALID_SOCKET; // // stop client discovery thread. // if( GlobalCliDiscoverThreadHandle != NULL ) { // // Wait for the client discovery thread to stop, but don't wait // for longer than THREAD_TERMINATION_TIMEOUT msecs (60 secs) // DWORD WaitStatus = WaitForSingleObject( GlobalCliDiscoverThreadHandle, THREAD_TERMINATION_TIMEOUT ); TcpsvcsDbgAssert( WaitStatus != WAIT_FAILED ); if( WaitStatus == WAIT_FAILED ) { TcpsvcsDbgPrint((DEBUG_ERRORS, "WaitForSingleObject call failed, %ld\n", GetLastError() )); } CloseHandle( GlobalCliDiscoverThreadHandle ); GlobalCliDiscoverThreadHandle = NULL; } if( GlobalWinsockStarted ) { WSACleanup(); GlobalWinsockStarted = FALSE; } TcpsvcsDbgAssert( GlobalNumNBPendingRecvs == 0) TcpsvcsDbgAssert( GlobalNBPendingRecvs == NULL ); if( GlobalNBPendingRecvs != NULL ) { SvclocHeap->Free( GlobalNBPendingRecvs ); } TcpsvcsDbgAssert( GlobalWin31NumNBResps == 0) TcpsvcsDbgAssert( IsListEmpty( &GlobalWin31NBRespList ) == TRUE ); // // free response list. // while ( !IsListEmpty( &GlobalWin31NBRespList ) ) { PLIST_ENTRY Entry; Entry = RemoveHeadList( &GlobalWin31NBRespList ); // // 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 ); } if( SvclocHeap != NULL ) { delete SvclocHeap; SvclocHeap = NULL; } if( GlobalDiscoveryInProgressEvent != NULL ) { CloseHandle( GlobalDiscoveryInProgressEvent ); GlobalDiscoveryInProgressEvent = NULL; } GlobalLastDiscoveryTime = 0; UNLOCK_SVC_GLOBAL_DATA(); DeleteCriticalSection( &GlobalSvclocCritSect ); #if DBG // // Delete dbg crit sect. // DeleteCriticalSection( &GlobalDebugCritSect ); #endif // DBG return( ERROR_SUCCESS ); } VOID FreeServiceInfo( LPINET_SERVICE_INFO ServiceInfo ) /*++ Routine Description: This function frees the memory blocks consumed by the service info structure. Arguments: ServiceInfo : pointer to a service info structure. Return Value: None. --*/ { TcpsvcsDbgAssert( ServiceInfo != NULL ); if( ServiceInfo == NULL ) { return; } // // free all leaves of the tree first and then branches. // // // free service comment. // if( ServiceInfo->ServiceComment != NULL ) { SvclocHeap->Free( ServiceInfo->ServiceComment ); } if( ServiceInfo->Bindings.NumBindings ) { TcpsvcsDbgAssert( ServiceInfo->Bindings.BindingsInfo != NULL ); if( ServiceInfo->Bindings.BindingsInfo != NULL ) { DWORD i; for( i = 0; i < ServiceInfo->Bindings.NumBindings; i++ ) { if( ServiceInfo->Bindings.BindingsInfo[i].BindData != NULL ) { SvclocHeap->Free( ServiceInfo->Bindings.BindingsInfo[i].BindData ); } } SvclocHeap->Free( ServiceInfo->Bindings.BindingsInfo ); } } else { TcpsvcsDbgAssert( ServiceInfo->Bindings.BindingsInfo == NULL ); } SvclocHeap->Free( ServiceInfo ); return; } VOID FreeServerInfo( LPINET_SERVER_INFO ServerInfo ) /*++ Routine Description: This function frees the memory blocks consumed by the server info structure. Arguments: ServerInfo : pointer to a server info structure. Return Value: None. --*/ { DWORD i; if( ServerInfo != NULL ) { // // first free all service info. // if( ServerInfo->Services.NumServices > 0 ) { TcpsvcsDbgAssert( ServerInfo->Services.Services != NULL ); } for ( i = 0; i < ServerInfo->Services.NumServices; i++) { FreeServiceInfo( ServerInfo->Services.Services[i] ); } // // now free services pointer array. // if( ServerInfo->Services.Services != NULL ) { SvclocHeap->Free( ServerInfo->Services.Services ); } // // free server address. // if( ServerInfo->ServerAddress.BindData != NULL ) { SvclocHeap->Free( ServerInfo->ServerAddress.BindData ); } // // free server name. // if( ServerInfo->ServerName != NULL ) { SvclocHeap->Free( ServerInfo->ServerName ); } // // now server info structure. // SvclocHeap->Free( ServerInfo ); } return; } VOID FreeServersList( LPINET_SERVERS_LIST ServersList ) /*++ Routine Description: This function frees the memory blocks consumed by the servers list structure. Arguments: ServersList : pointer to a servers liststructure. Return Value: None. --*/ { if( ServersList != NULL ) { // // free server info structures. // if( ServersList->NumServers > 0 ) { TcpsvcsDbgAssert( ServersList->Servers != NULL ); } DWORD i; for( i = 0; i < ServersList->NumServers; i++ ) { FreeServerInfo( ServersList->Servers[i] ); } // // free servers info pointer array. // if( ServersList->Servers != NULL ) { SvclocHeap->Free( ServersList->Servers ); } // // servers list structure. // SvclocHeap->Free( ServersList ); } return; } BOOL GetNetBiosLana( PLANA_ENUM pLanas ) /*++ Routine Description: This function enumurate all netbios lana on the system. Arguments: pLanas - pointer to LANA_ENUM structure where enum is returned. Return Value: TRUE - if successed. FALSE - otherwise. --*/ { NCB NetBiosNCB; UCHAR NBErrorCode; memset( &NetBiosNCB, 0, sizeof(NetBiosNCB) ); NetBiosNCB.ncb_command = NCBENUM; NetBiosNCB.ncb_buffer = (PUCHAR)pLanas; NetBiosNCB.ncb_length = sizeof(LANA_ENUM); NBErrorCode = Netbios( &NetBiosNCB ); if( (NBErrorCode == NRC_GOODRET) && (NetBiosNCB.ncb_retcode == NRC_GOODRET) ) { return( TRUE ); } TcpsvcsDbgPrint(( DEBUG_ERRORS, "NetBios() failed, %ld, %ld \n", NBErrorCode, NetBiosNCB.ncb_retcode )); return( FALSE ); } BOOL GetEnumNBLana( PLANA_ENUM pLanas ) /*++ Routine Description: This function enumurate all netbios lana on the system. Arguments: pLanas - pointer to LANA_ENUM structure where enum is returned. Return Value: TRUE - if successed. FALSE - otherwise. --*/ { DWORD Error; INT ProtocolCount; PPROTOCOL_INFO ProtocolBuffer = NULL; DWORD ProtocolBufferSize = 0; // // init return value. // pLanas->length = 0; // // determine the enum buffer size required. // ProtocolCount = EnumProtocols( NULL, NULL, &ProtocolBufferSize ); if( ProtocolCount == SOCKET_ERROR ) { Error = WSAGetLastError(); if( Error != ERROR_INSUFFICIENT_BUFFER ) { goto Cleanup; } } if( (ProtocolBufferSize == 0) || (ProtocolCount == 0) ) { Error = ERROR_SUCCESS; goto Cleanup; } // // allocate memory for the protocol buffer. // ProtocolBuffer = (PPROTOCOL_INFO)SvclocHeap->Alloc( ProtocolBufferSize ); if( ProtocolBuffer == NULL ) { Error = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } // // now enum protocols. // ProtocolCount = EnumProtocols( NULL, ProtocolBuffer, &ProtocolBufferSize ); if( ProtocolCount == SOCKET_ERROR ) { Error = WSAGetLastError(); goto Cleanup; } TcpsvcsDbgAssert( ProtocolCount > 0 ); // // now filter net bios protcols only and get the corresponding lana // values. // DWORD i; for ( i = 0; i < (DWORD)ProtocolCount; i++ ) { if( ProtocolBuffer[i].iAddressFamily == AF_NETBIOS ) { if( pLanas->length < MAX_LANA ) { UCHAR Lana; DWORD j; Lana = (UCHAR)((INT)ProtocolBuffer[i].iProtocol * (-1)); // // if this is a new lana add to list. // for ( j = 0; j < pLanas->length ; j++ ) { if( pLanas->lana[j] == Lana ) { break; } } if( j >= pLanas->length ) { pLanas->lana[pLanas->length] = Lana; pLanas->length++; } } } } Error = ERROR_SUCCESS; Cleanup: if( ProtocolBuffer != NULL ) { SvclocHeap->Free( ProtocolBuffer ); } if( Error != ERROR_SUCCESS ) { TcpsvcsDbgPrint(( DEBUG_ERRORS, "GetNetBiosLana failed, %ld\n", Error )); return( FALSE ); } return( TRUE ); } BOOL MakeNBSocketForLana( UCHAR Lana, PSOCKADDR pSocketAddress, SOCKET *pNBSocket ) /*++ Routine Description: This function possibly creates a socket for the given lana and binds to the given socket address. ASSUME : global data crit sect is locked. Arguments: Lana : lana number for the new sockets. pSocketAddress : pointer to a socket address to bind to. pNBSocket : pointer to a location where the new socket is returned. Return Value: TRUE : if successfully created a socket and bound to the given nb addresse. FALSE : otherwise. --*/ { DWORD Error; SOCKET NBSocket; DWORD Arg = 1; *pNBSocket = INVALID_SOCKET; // // create a socket for this lana. // NBSocket = socket( AF_NETBIOS, SOCK_DGRAM, Lana ); if( NBSocket == INVALID_SOCKET ) { Error = WSAGetLastError(); TcpsvcsDbgPrint(( DEBUG_ERRORS, "socket() failed, %ld\n", Error )); // // something wrong with this lana, try rest. // return( FALSE ); } // // make this socket non blocking. // if( (ioctlsocket( NBSocket, FIONBIO, &Arg )) == SOCKET_ERROR ) { Error = WSAGetLastError(); TcpsvcsDbgPrint(( DEBUG_ERRORS, "ioctlsocket() failed, %ld\n", Error )); // // something wrong with this lana, try rest. // closesocket( NBSocket ); return( FALSE ); } // // bind to this socket. // if( bind( NBSocket, pSocketAddress, sizeof(SOCKADDR_NB) ) == SOCKET_ERROR ) { Error = WSAGetLastError(); TcpsvcsDbgPrint(( DEBUG_ERRORS, "ioctlsocket() failed, %ld\n", Error )); // // something wrong with this lana, try rest. // closesocket( NBSocket ); return( FALSE ); } *pNBSocket = NBSocket; return( TRUE ); }