|
|
/*
* Purpose: C++ API for finding the telephony * servers in Active Directory * */
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <windows.h>
#include <objbase.h>
#include <activeds.h>
#include "tspi.h"
#include "tapi.h"
#include "dslookup.h"
#include "utils.h"
#include "tchar.h"
const TCHAR gszNoDSQuery[] = TEXT("NoDSQuery"); const TCHAR gszStatusActive[] = TEXT("S{Active}"); const TCHAR gszTTLWithBrace[] = TEXT("TTL{");
//
// Utility functions
//
//
// GetIntFromString
// Utility function used for retrieving TTL information
// Parameters:
// sz - String that contains the integer
// dwDigits - Number of digits to convert
//
int GetIntFromString (LPTSTR & sz, DWORD dwDigits) { int iRet = 0;
while (*sz != 0 && dwDigits) { iRet = ((iRet << 3) + (iRet << 1)) + // IRet * 10
+ (*sz - '0'); ++sz; --dwDigits; }
return iRet; }
//
// Rules:
// The following codes are not thread safe, the caller
// needs to be concious about synchronization.
// Currently theses are only used in remotesp.tsp
//
/**********************************************************
* Get TAPI servers list from the registry *********************************************************/
DWORD gdwCurServerNum = 0; HKEY ghRegistry = NULL;
BOOL RegOpenServerLookup( HKEY hRegistry ) { if (NULL != ghRegistry) { // Already have a search in progress or the
// caller did not close the last search.
return FALSE; } else { ghRegistry = hRegistry; return TRUE; } }
BOOL RegGetNextServer( LPTSTR pszServerName, DWORD dwSize ) { DWORD dwRet; LOG((TL_TRACE, "GetNextServer entered"));
TCHAR szServerN[24]; DWORD dwType;
wsprintf(szServerN, TEXT("Server%d"), gdwCurServerNum++);
LOG((TL_INFO, "RegGetNextServer: Getting server %d from reg", gdwCurServerNum-1));
dwRet = RegQueryValueEx( ghRegistry, szServerN, 0, &dwType, (LPBYTE) pszServerName, &dwSize ); if (ERROR_SUCCESS != dwRet) { LOG((TL_INFO, "Got last server")); LOG((TL_TRACE, "GetNextServer exited")); return FALSE; }
LOG((TL_TRACE, "GetNextServer exited")); return TRUE; }
BOOL RegCloseLookup( void ) { LOG((TL_INFO, "Closing directory lookup"));
ghRegistry = NULL; gdwCurServerNum = 0; return TRUE; }
/**********************************************************
* Enumerate published telephony servers *********************************************************/
typedef struct _TAPISRV_LOOKUP_CTX { ADS_SEARCH_HANDLE hDirSearch; IDirectorySearch * pDirSearch; } TAPISRV_LOOKUP_CTX, *PTAPISRV_LOOKUP_CTX;
// gszTapisrvGuid needs to be consistant with server\dspub.cpp
const WCHAR gszTapisrvGuid[] = L"keywords=B1A37774-E3F7-488E-ADBFD4DB8A4AB2E5";
//
// GetGC
//
// Retrieve the IDirectorySearch of the Global Catalog (GC)
// for SCP maintenance / discovery
//
HRESULT GetGC (IDirectorySearch ** ppGC) { HRESULT hr = S_OK; IEnumVARIANT * pEnum = NULL; IADsContainer * pCont = NULL; VARIANT var; IDispatch * pDisp = NULL; ULONG lFetch;
// Set IDirectorySearch pointer to NULL.
*ppGC = NULL;
// First, bind to the GC: namespace container object. The "real" GC DN
// is a single immediate child of the GC: namespace, which must
// be obtained using enumeration.
hr = ADsOpenObject( TEXT("GC:"), NULL, NULL, ADS_SECURE_AUTHENTICATION, //Use Secure Authentication
IID_IADsContainer, (void**)&pCont ); if (FAILED(hr)) { LOG((TL_ERROR, "ADsOpenObject failed: 0x%x\n", hr)); goto cleanup; }
// Fetch an enumeration interface for the GC container.
hr = ADsBuildEnumerator(pCont, &pEnum); if (FAILED(hr)) { LOG((TL_ERROR, "ADsBuildEnumerator failed: 0x%x\n", hr)); goto cleanup; }
//Now enumerate. There's only one child of the GC: object.
hr = ADsEnumerateNext(pEnum, 1, &var, &lFetch); if (FAILED(hr)) { LOG((TL_ERROR, "ADsEnumerateNext failed: 0x%x\n", hr)); goto cleanup; }
if (( hr == S_OK ) && ( lFetch == 1 ) ) { pDisp = V_DISPATCH(&var); hr = pDisp->QueryInterface( IID_IDirectorySearch, (void**)ppGC); }
cleanup: if (pEnum) { ADsFreeEnumerator(pEnum); } if (pCont) { pCont->Release(); } if (pDisp) { (pDisp)->Release(); } return hr; }
//
// DsOpenServerLookup
// Start the operation of tapisrv server lookup
// pctx - The lookup context
//
HRESULT DsOpenServerLookup( PTAPISRV_LOOKUP_CTX pctx ) { HRESULT hr = S_OK; ADS_SEARCHPREF_INFO SearchPref[3]; BOOL bInited = FALSE; DWORD dwPref;
WCHAR *szAttribs[]={ L"distinguishedName" };
if (pctx == NULL) { hr = E_INVALIDARG; goto ExitHere; }
pctx->hDirSearch = NULL; pctx->pDirSearch = NULL;
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED); if (FAILED (hr)) { goto ExitHere; } bInited = TRUE;
// Get the global catalog
hr = GetGC (&pctx->pDirSearch); if (FAILED (hr) || pctx->pDirSearch == NULL) { goto ExitHere; }
// Set up the search. We want to do a deep search.
// Note that we are not expecting thousands of objects
// in this example, so we will ask for 10 rows / page.
dwPref=sizeof(SearchPref)/sizeof(ADS_SEARCHPREF_INFO); SearchPref[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; SearchPref[0].vValue.dwType = ADSTYPE_INTEGER; SearchPref[0].vValue.Integer = ADS_SCOPE_SUBTREE;
SearchPref[1].dwSearchPref = ADS_SEARCHPREF_PAGESIZE; SearchPref[1].vValue.dwType = ADSTYPE_INTEGER; SearchPref[1].vValue.Integer = 10;
SearchPref[2].dwSearchPref = ADS_SEARCHPREF_TIME_LIMIT; SearchPref[2].vValue.dwType = ADSTYPE_INTEGER; SearchPref[2].vValue.Integer = 5 * 60; // 5 minute search timeout
hr = pctx->pDirSearch->SetSearchPreference(SearchPref, dwPref); if (FAILED(hr)) { LOG((TL_ERROR, "Failed to set search prefs: hr:0x%x\n", hr)); goto ExitHere; }
// Now execute the search
hr = pctx->pDirSearch->ExecuteSearch( (LPWSTR)gszTapisrvGuid, szAttribs, sizeof(szAttribs) / sizeof(WCHAR *), &pctx->hDirSearch );
ExitHere: if (FAILED(hr) && pctx != NULL) { if (pctx->pDirSearch) { pctx->pDirSearch->Release(); } } if (FAILED(hr) && bInited) { CoUninitialize (); } return hr; }
//
// DsGetNextServer
//
// Return the next server name (in ANSI since that is what the
// RPC subsystem uses)
//
// returns S_FALSE if no more server to enumerate
//
HRESULT DsGetNextServer( PTAPISRV_LOOKUP_CTX pctx, LPTSTR pszServerName, DWORD dwSize ) { HRESULT hr = S_OK; ADS_SEARCH_COLUMN Col; TCHAR szDN[MAX_PATH]; WCHAR *szAttribs[]={ L"serviceDNSName", L"serviceBindingInformation", }; ADS_ATTR_INFO *pPropEntries = NULL; DWORD dwNumAttrGot; IDirectoryObject * pSCP = NULL; int i; LPWSTR wsz; BOOL bCheckedBinding;
if (pctx == NULL || pctx->pDirSearch == NULL || pctx->hDirSearch == NULL || dwSize < sizeof(WCHAR)) { hr = E_INVALIDARG; goto ExitHere; }
hr = pctx->pDirSearch->GetNextRow(pctx->hDirSearch); if (SUCCEEDED (hr) && hr != S_ADS_NOMORE_ROWS) { hr = pctx->pDirSearch->GetColumn( pctx->hDirSearch, L"distinguishedName", &Col ); if (FAILED (hr)) { goto ExitHere; } _tcscpy (szDN, TEXT("LDAP://")); _tcsncpy ( szDN + _tcslen (szDN), Col.pADsValues->CaseExactString, sizeof(szDN)/sizeof(TCHAR) - _tcslen (szDN) ); pctx->pDirSearch->FreeColumn(&Col); hr = ADsGetObject ( szDN, IID_IDirectoryObject, (void **)&pSCP );
if (FAILED(hr)) { LOG((TL_ERROR, "DsGetNextServer: ADsGetObject %S failed", szDN)); hr = S_FALSE; // return a non-critical error so that we
// ignore this server but don't abandon the search
goto ExitHere; } LOG((TL_TRACE, "DsGetNextServer: ADsGetObject %S succeeded", szDN)); hr = pSCP->GetObjectAttributes ( szAttribs, sizeof(szAttribs) / sizeof(WCHAR *), &pPropEntries, &dwNumAttrGot ); if (FAILED(hr) || dwNumAttrGot != sizeof(szAttribs) / sizeof(WCHAR *)) { LOG((TL_ERROR, "DsGetNextServer: GetObjectAttributes %S failed", szDN)); hr = S_FALSE; goto ExitHere; } LOG((TL_TRACE, "DsGetNextServer: GetObjectAttributes %S succeeded", szDN));
bCheckedBinding = FALSE; for (i=0;i<(int)dwNumAttrGot;i++) { if (_tcsicmp(TEXT("serviceDNSName"), pPropEntries[i].pszAttrName) ==0 && (pPropEntries[i].pADsValues->dwType == ADSTYPE_CASE_EXACT_STRING || pPropEntries[i].pADsValues->dwType == ADSTYPE_CASE_IGNORE_STRING)) { _tcsncpy ( pszServerName, pPropEntries[i].pADsValues->CaseExactString, dwSize/sizeof(TCHAR) ); pszServerName[dwSize/sizeof(TCHAR) - 1] = '\0'; if (bCheckedBinding) { break; } } else if (_tcsicmp(TEXT("serviceBindingInformation"), pPropEntries[i].pszAttrName) ==0 && (pPropEntries[i].pADsValues->dwType == ADSTYPE_CASE_EXACT_STRING || pPropEntries[i].pADsValues->dwType == ADSTYPE_CASE_IGNORE_STRING)) { SYSTEMTIME st; FILETIME ft1, ft2;
bCheckedBinding = TRUE; wsz = pPropEntries[i].pADsValues->CaseExactString; wsz = wcsstr (wsz, gszStatusActive); if (wsz == NULL) { // No server status information or server is not active
// ignore this server
LOG((TL_ERROR, "DsGetNextServer: %S No server status information", szDN)); hr = S_FALSE; break; } wsz += _tcslen(gszStatusActive); // skip "S{Active}" itself
wsz = wcsstr (wsz, gszTTLWithBrace); if (wsz == NULL) { // No TTL found, corrupt serviceBindingInformation, ignore
LOG((TL_ERROR, "DsGetNextServer: %S No TTL found", szDN)); hr = S_FALSE; break; } wsz += _tcslen (gszTTLWithBrace); // skip "TTL{"
//
// The following codes parses the TTL information
// created in server\dspub.cpp. They need to be
// consistant. The current format is 5 digits for
// year & 3 digits for milliseconds, 2 digits for
// the remaining
//
st.wYear = (WORD) GetIntFromString (wsz, 5); st.wMonth = (WORD) GetIntFromString (wsz, 2); st.wDay = (WORD) GetIntFromString (wsz, 2), st.wHour = (WORD) GetIntFromString (wsz, 2); st.wMinute = (WORD) GetIntFromString (wsz, 2); st.wSecond = (WORD) GetIntFromString (wsz, 2); st.wMilliseconds = (WORD) GetIntFromString (wsz, 3); SystemTimeToFileTime (&st, &ft1); GetSystemTimeAsFileTime (&ft2); if (CompareFileTime (&ft1, &ft2) < 0) { // The TapiSCP object has passed its TTL, ignore
hr = S_FALSE; LOG((TL_ERROR, "DsGetNextServer: %S The TapiSCP object has passed its TTL", szDN)); break; } } } if (i == (int) dwNumAttrGot) { // Did not find an attribute
hr = S_FALSE; } }
ExitHere: if (pSCP) pSCP->Release();
if (pPropEntries) FreeADsMem(pPropEntries);
return hr; }
//
// DsCloseLookup
// Close the TAPI DS published server lookup identified by pctx
//
HRESULT DsCloseLookup( PTAPISRV_LOOKUP_CTX pctx ) { if (pctx && pctx->pDirSearch && pctx->hDirSearch) { pctx->pDirSearch->CloseSearchHandle(pctx->hDirSearch); } if (pctx && pctx->pDirSearch) { pctx->pDirSearch->Release(); } CoUninitialize (); return S_OK; }
/**********************************************************
* Get TAPI servers list remotesp.tsp should contact * Servers include those specified in registry through * tcmsetup.exe and those servers published in the DS *********************************************************/
typedef struct _SERVER_LOOKUP_ENTRY { TCHAR szServer[MAX_PATH]; BOOL bFromReg; } SERVER_LOOKUP_ENTRY, *PSERVER_LOOKUP_ENTRY;
typedef struct _SERVER_LOOKUP { DWORD dwTotalEntries; DWORD dwUsedEntries; SERVER_LOOKUP_ENTRY * aEntries; } SERVER_LOOKUP, *PSERVER_LOOKUP;
SERVER_LOOKUP gLookup; DWORD gdwCurIndex;
//
// AddEntry : return FALSE if failed; otherwise, return true
//
BOOL AddEntry ( LPTSTR szServer, BOOL bFromReg ) { LPTSTR psz; if (gLookup.dwUsedEntries >= gLookup.dwTotalEntries) { PSERVER_LOOKUP_ENTRY pNew;
pNew = (PSERVER_LOOKUP_ENTRY) DrvAlloc ( sizeof(SERVER_LOOKUP_ENTRY) * (gLookup.dwTotalEntries + 5) ); if (pNew == NULL) { return FALSE; } if (gLookup.dwUsedEntries > 0) { CopyMemory ( pNew, gLookup.aEntries, sizeof(SERVER_LOOKUP_ENTRY) * gLookup.dwTotalEntries ); } if (gLookup.aEntries) { DrvFree (gLookup.aEntries); } gLookup.aEntries = pNew; gLookup.dwTotalEntries += 5; } wcsncpy ( gLookup.aEntries[gLookup.dwUsedEntries].szServer, szServer, sizeof(gLookup.aEntries[gLookup.dwUsedEntries].szServer)/sizeof(TCHAR) ); psz = _tcschr(gLookup.aEntries[gLookup.dwUsedEntries].szServer, TEXT('.')); if (psz != NULL) { *psz = 0; } gLookup.aEntries[gLookup.dwUsedEntries].bFromReg = bFromReg; ++gLookup.dwUsedEntries; return TRUE; }
BOOL IsServerInListOrSelf ( LPTSTR szServer ) { int i; TCHAR szServer1[MAX_PATH + 1]; LPTSTR psz; BOOL bRet = FALSE;
_tcsncpy (szServer1, szServer, MAX_PATH); szServer1[MAX_PATH] = 0;
// A computer name might be DNS name like comp1.microsoft.com
// only compare the computer name
psz = _tcschr(szServer1, TEXT('.')); if (psz != NULL) { *psz = 0; } for (i = 0; i < (int)gLookup.dwUsedEntries; ++i) { if (_tcsicmp (szServer1, gLookup.aEntries[i].szServer) == 0) { bRet = TRUE; break; } }
if (!bRet) { TCHAR szSelf[MAX_PATH]; DWORD dwSize = sizeof(szSelf); if (GetComputerName (szSelf, &dwSize)) { if (_tcsicmp (szServer1, szSelf) == 0) { bRet = TRUE; } } }
return bRet; }
BOOL OpenServerLookup ( HKEY hRegistry ) { BOOL bRet = TRUE; TCHAR szServer[MAX_PATH]; TAPISRV_LOOKUP_CTX ctx; HRESULT hr; DWORD dwNoDSQuery = 0; DWORD dwSize = sizeof(dwNoDSQuery);
gLookup.dwTotalEntries = 0; gLookup.dwUsedEntries = 0; gLookup.aEntries = NULL;
//
// First add the computer from registry
//
if (RegOpenServerLookup (hRegistry)) { while (RegGetNextServer (szServer, sizeof(szServer))) { if (!IsServerInListOrSelf (szServer)) { AddEntry (szServer, TRUE); } } RegCloseLookup (); }
if (hRegistry != NULL) { if (ERROR_SUCCESS != RegQueryValueEx ( hRegistry, gszNoDSQuery, NULL, NULL, (LPBYTE)&dwNoDSQuery, &dwSize )) { dwNoDSQuery = 0; } }
//
// Next add the computer from DS unless disabled
//
if (dwNoDSQuery == 0) { if (DsOpenServerLookup (&ctx) == S_OK) { while (SUCCEEDED(hr = DsGetNextServer (&ctx,szServer, sizeof(szServer)))) { if (hr == S_ADS_NOMORE_ROWS) { break; } else if (hr != S_OK) { continue; // Server needs to be ignored
} if (szServer[0] != 0 && !IsServerInListOrSelf (szServer)) { AddEntry (szServer, FALSE); } } DsCloseLookup (&ctx); } }
gdwCurIndex = 0;
return TRUE; }
BOOL GetNextServer ( LPSTR szServer, DWORD dwSize, BOOL * pbReg ) { BOOL bRet = TRUE; DWORD dwRet;
if (gdwCurIndex >= gLookup.dwUsedEntries) { bRet = FALSE; goto ExitHere; } if (pbReg != NULL) { *pbReg = gLookup.aEntries[gdwCurIndex].bFromReg; } dwRet = WideCharToMultiByte( GetACP(), 0, gLookup.aEntries[gdwCurIndex].szServer, -1, szServer, dwSize, 0, NULL ); if (dwRet == 0) { bRet = FALSE; goto ExitHere; } ++gdwCurIndex;
ExitHere: return bRet; }
BOOL CloseLookup ( void ) { if (gLookup.aEntries) { DrvFree (gLookup.aEntries); } gLookup.aEntries = NULL; gLookup.dwTotalEntries = 0; gLookup.dwUsedEntries = 0; gdwCurIndex = 0;
return TRUE; }
HRESULT SockStartup ( RSPSOCKET * pSocket ) { HRESULT hr = S_OK; BOOL bCleanup = FALSE; WSADATA wsadata; WORD wVersionRequested = MAKEWORD( 2, 2 );
if (pSocket == NULL) { hr = LINEERR_INVALPARAM; goto ExitHere; } ZeroMemory (pSocket, sizeof(RSPSOCKET)); bCleanup = TRUE;
ZeroMemory (pSocket, sizeof(RSPSOCKET));
pSocket->hWS2 = LoadLibrary (TEXT("ws2_32.dll")); if (pSocket->hWS2 == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ExitHere; } pSocket->pFnWSAStartup = (PFNWSASTARTUP)GetProcAddress ( pSocket->hWS2, "WSAStartup" ); pSocket->pFnWSACleanup = (PFNWSACLEANUP)GetProcAddress ( pSocket->hWS2, "WSACleanup" ); pSocket->pFngethostbyname = (PFNGETHOSTBYNAME)GetProcAddress( pSocket->hWS2, "gethostbyname" ); if (pSocket->pFnWSAStartup == NULL || pSocket->pFnWSACleanup == NULL || pSocket->pFngethostbyname == NULL) { hr = LINEERR_OPERATIONFAILED; goto ExitHere; }
pSocket->hICMP = LoadLibrary (TEXT("icmp.dll")); if (pSocket->hICMP == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); goto ExitHere; } pSocket->pFnIcmpCreateFile = (PFNICMPCREATEFILE)GetProcAddress ( pSocket->hICMP, "IcmpCreateFile" ); pSocket->pFnIcmpCloseHandle = (PFNICMPCLOSEHANDLE)GetProcAddress ( pSocket->hICMP, "IcmpCloseHandle" ); pSocket->pFnIcmpSendEcho = (PFNICMPSENDECHO)GetProcAddress ( pSocket->hICMP, "IcmpSendEcho" ); if (pSocket->pFnIcmpCreateFile == NULL || pSocket->pFnIcmpCloseHandle == NULL || pSocket->pFnIcmpSendEcho == NULL) { hr = LINEERR_OPERATIONFAILED; goto ExitHere; }
hr = (*pSocket->pFnWSAStartup)( wVersionRequested, &wsadata ); if(FAILED(hr)) { goto ExitHere; }
pSocket->IcmpHandle = (*pSocket->pFnIcmpCreateFile)(); if (pSocket->IcmpHandle == INVALID_HANDLE_VALUE) { (*pSocket->pFnWSACleanup)(); hr = LINEERR_OPERATIONFAILED; }
ExitHere: if (hr != S_OK && bCleanup) { if (pSocket->hWS2 != NULL) { FreeLibrary (pSocket->hWS2); } if (pSocket->hICMP != NULL) { FreeLibrary (pSocket->hICMP); } ZeroMemory (pSocket, sizeof(RSPSOCKET)); } return hr; }
#define MAX_PACKET_SIZE 256
#define PING_TIMEOUT 1000
HRESULT SockIsServerResponding ( RSPSOCKET * pSocket, char * szServer ) { HRESULT hr = S_OK; unsigned long inetAddr; HOSTENT * pHost; BOOL bRet; CHAR ReplyBuf[MAX_PACKET_SIZE]; // Validate parameters
if (pSocket == NULL || pSocket->hWS2 == NULL || pSocket->hICMP == NULL || pSocket->IcmpHandle == NULL || pSocket->IcmpHandle == INVALID_HANDLE_VALUE) { hr = LINEERR_INVALPARAM; goto ExitHere; }
// Get the server IP address
pHost = (*pSocket->pFngethostbyname)(szServer); if (pHost == NULL) { hr = LINEERR_OPERATIONFAILED; goto ExitHere; } inetAddr = *(unsigned long *)pHost->h_addr;
// Ping the server
bRet = (*pSocket->pFnIcmpSendEcho)( pSocket->IcmpHandle, inetAddr, 0, 0, 0, (LPVOID)ReplyBuf, sizeof(ReplyBuf), PING_TIMEOUT ); if (!bRet || ((PICMP_ECHO_REPLY)ReplyBuf)->Address != inetAddr) { hr = S_FALSE; } ExitHere: return hr; }
HRESULT SockShutdown ( RSPSOCKET * pSocket ) { if (pSocket != NULL) { if (pSocket->IcmpHandle != INVALID_HANDLE_VALUE && pSocket->IcmpHandle != NULL) { (*pSocket->pFnIcmpCloseHandle)(pSocket->IcmpHandle); } if (pSocket->hICMP != NULL) { FreeLibrary (pSocket->hICMP); } if (pSocket->hWS2 != NULL) { (*pSocket->pFnWSACleanup)(); FreeLibrary (pSocket->hWS2); } ZeroMemory (pSocket, sizeof(RSPSOCKET)); } return S_OK; }
|