windows-nt/Source/XPSP1/NT/com/svcdlls/trksvcs/trksvr/dbconn.cxx
2020-09-26 16:20:57 +08:00

244 lines
6.9 KiB
C++

// Copyright (c) 1996-1999 Microsoft Corporation
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// File: dbconn.cxx
//
// Contents: Shared database initialization code.
//
// Classes:
//
// Functions:
//
//
//
// History: 18-Nov-96 BillMo Created.
//
// Notes:
//
// Codework:
//
//--------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include "trksvr.hxx"
// LDAP version
void
CDbConnection::Initialize(CSvcCtrlInterface * psvc, OPTIONAL const TCHAR *ptszHostName )
{
int err;
LDAPMessage *pRes = NULL;
TCHAR ** ppszNamingContexts = NULL;
int tries = 0;
TCHAR tszLocalHostName[ MAX_COMPUTERNAME_LENGTH + 1 ];
_fInitializeCalled = TRUE;
_pszBaseDn = NULL;
__try
{
if( NULL == ptszHostName )
{
CMachineId(MCID_LOCAL).GetName( tszLocalHostName, ELEMENTS(tszLocalHostName) );
ptszHostName = tszLocalHostName;
}
TrkLog((TRKDBG_SVR, TEXT("ldap_init(%s, LDAP_PORT)"), ptszHostName ));
_pldap = ldap_init( const_cast<TCHAR*>(ptszHostName), LDAP_PORT);
if( NULL == _pldap )
{
TrkLog(( TRKDBG_ERROR, TEXT("CDbConnection failed ldap_init (%lu)"),
GetLastError() ));
TrkRaiseLastError();
}
// Set the option telling LDAP that we gave it an explicit DC name and
// that it can avoid the DsGetDcName.
LONG LdapOption = PtrToLong(LDAP_OPT_ON);
err = ldap_set_optionW( _pldap, LDAP_OPT_AREC_EXCLUSIVE, &LdapOption );
if( LDAP_SUCCESS != err )
{
TrkLog(( TRKDBG_ERROR,
TEXT("Failed ldap_set_option (LDAP_OPT_AREC_EXCLUSIVE) - %ld"),
err ));
TrkRaiseException( LdapMapErrorToWin32(err) );
}
// Note: This method used to do an ldap_open, but that function has been
// deprecated. The problem in NT5, was that during bootup call to ldap_open
// the DS occasionally wasn't yet available. Thus the logic below was added
// to do retries.
// Really, all of this code should go away except for the ldap_init, since
// ldap_connect is called implicitely by all the ldap apis. But to minimize
// the risk, the code has been left basically unchanged, other than using
// ldap_init/ldap_connect rather than ldap_open. If this code needs to be
// modified at any point, it should be reworked to remove the ldap_connect.
LDAP_TIMEVAL Timeout;
Timeout.tv_sec = 2;
Timeout.tv_usec = 0;
retry:
err = ldap_connect( _pldap, &Timeout );
if( LDAP_SUCCESS != err )
{
if (tries++ < 10)
{
TrkLog((TRKDBG_ERROR, TEXT("ldap_open returned NULL, now sleeping...")));
if (psvc != NULL)
psvc->UpdateWaitHint(30000);
goto retry;
}
TrkLog((TRKDBG_ERROR, TEXT("CDbConnection::Initialize() - failed :-(")));
TrkRaiseLastError( );
}
// search to get default base DN
err = ldap_bind_s(_pldap,
NULL, // DN of what ? system account object ?
NULL, // we're running as system, so use our credentials
LDAP_AUTH_SSPI);
if (err != LDAP_SUCCESS)
{
TrkLog((TRKDBG_ERROR, TEXT("CDbConnection::Initialize() - ldap_bind_s failed")));
TrkRaiseWin32Error( LdapMapErrorToWin32(err) );
}
TCHAR *aszNamingContexts[2] = { TEXT("NamingContexts"), NULL };
err = ldap_search_s(_pldap,
NULL, // searching of tree
LDAP_SCOPE_BASE,
TEXT("(objectclass=*)"),
aszNamingContexts,
0,
&pRes);
if (err != LDAP_SUCCESS)
{
TrkLog((TRKDBG_ERROR, TEXT("CDbConnection::Initialize() - ldap_search_s failed (%lu)"), err ));
TrkRaiseException( TRK_E_DB_CONNECT_ERROR );
}
if (ldap_count_entries(_pldap, pRes) == 0)
{
TrkLog((TRKDBG_ERROR, TEXT("CDbConnection::Initialize() - ldap_count_entries found no entries (%lu)"), err ));
TrkRaiseException( TRK_E_DB_CONNECT_ERROR );
}
LDAPMessage * pEntry = ldap_first_entry(_pldap, pRes);
if (pEntry == NULL)
{
TrkLog((TRKDBG_ERROR, TEXT("CDbConnection::Initialize() - ldap_first_entry failed (%lu)"), err ));
TrkRaiseWin32Error(LdapMapErrorToWin32(_pldap->ld_errno));
}
int l;
ppszNamingContexts = ldap_get_values(_pldap, pEntry, TEXT("NamingContexts"));
if (ppszNamingContexts == NULL ||
ppszNamingContexts[0] == NULL ||
(l=_tcslen(ppszNamingContexts[0])) == 0)
{
TrkLog((TRKDBG_ERROR, TEXT("CDbConnection::Initialize() - couldn't find 'NamingContexts'")));
TrkRaiseWin32Error(LdapMapErrorToWin32(_pldap->ld_errno));
}
for (int i=0; i<l-4; i++)
{
if (memcmp(&ppszNamingContexts[0][i],
TEXT("DC="),
3*sizeof(TCHAR)) == 0)
{
break;
}
}
if (i == l-3)
{
TrkLog((TRKDBG_ERROR, TEXT("CDbConnection::Initialize() - couldn't find 'DC'")));
TrkRaiseException( TRK_E_DB_CONNECT_ERROR );
}
_pszBaseDn = new TCHAR [l-i+1];
if (_pszBaseDn == NULL)
{
TrkLog((TRKDBG_ERROR, TEXT("CDbConnection::Initialize() - out of memory")));
TrkRaiseWin32Error(ERROR_NOT_ENOUGH_MEMORY);
}
_tcscpy(_pszBaseDn, &ppszNamingContexts[0][i]);
}
__finally
{
if (pRes)
ldap_msgfree(pRes);
if (ppszNamingContexts)
ldap_value_free(ppszNamingContexts);
}
}
void
CDbConnection::UnInitialize()
{
if (_fInitializeCalled)
{
if (_pldap != NULL)
{
// There is no ldap_close. Call ldap_unbind, even if ldap_bind
// wasn't called.
ldap_unbind( _pldap );
_pldap = NULL;
}
if (_pszBaseDn)
{
delete [] _pszBaseDn;
_pszBaseDn = NULL;
}
_fInitializeCalled = FALSE;
}
}
LDAP *
CDbConnection::Ldap()
{
// The critsec initialization may have failed.
if( !_cs.IsInitialized() )
_cs.Initialize(); // Raises on error
_cs.Enter();
if (!_fInitializeCalled)
{
__try
{
Initialize(NULL);
}
__finally
{
if (AbnormalTermination())
{
UnInitialize();
_cs.Leave();
}
}
}
_cs.Leave();
return(_pldap);
}