425 lines
14 KiB
C++
425 lines
14 KiB
C++
//--------------------------------------------------------------------
|
|
// DcInfo - implementation
|
|
// Copyright (C) Microsoft Corporation, 1999
|
|
//
|
|
// Created by: Louis Thomas (louisth), 7-8-99
|
|
//
|
|
// Gather information about the DCs in a domain
|
|
|
|
#include "pch.h" // precompiled headers
|
|
|
|
#include "DcInfo.h"
|
|
|
|
//####################################################################
|
|
// module private functions
|
|
|
|
//--------------------------------------------------------------------
|
|
// Get a list of DCs in this domain from the DS on an up DC.
|
|
MODULEPRIVATE HRESULT GetDcListFromDs(const WCHAR * wszDomainName, DcInfo ** prgDcs, unsigned int * pnDcs)
|
|
{
|
|
HRESULT hr;
|
|
NET_API_STATUS dwNetStatus;
|
|
DWORD dwDcCount;
|
|
unsigned int nIndex;
|
|
unsigned int nDcs;
|
|
unsigned int nDcIndex;
|
|
|
|
// varaibles that must be cleaned up
|
|
DOMAIN_CONTROLLER_INFOW * pDcInfo=NULL;
|
|
HANDLE hDs=NULL;
|
|
DS_DOMAIN_CONTROLLER_INFO_1W * rgDsDcInfo=NULL;
|
|
DcInfo * rgDcs=NULL;
|
|
|
|
// initialize out variables
|
|
*prgDcs=NULL;
|
|
*pnDcs=0;
|
|
|
|
// Get a DC to seed the algorithm with
|
|
dwNetStatus=DsGetDcName(
|
|
NULL, // computer name
|
|
wszDomainName, // domain name
|
|
NULL, // domain guid
|
|
NULL, // site name
|
|
DS_DIRECTORY_SERVICE_PREFERRED, // flags
|
|
&pDcInfo); // DC info
|
|
if (NO_ERROR!=dwNetStatus) {
|
|
hr=HRESULT_FROM_WIN32(dwNetStatus);
|
|
_JumpError(hr, error, "DsGetDcName");
|
|
}
|
|
if (0==(pDcInfo->Flags&DS_DS_FLAG)) {
|
|
hr=HRESULT_FROM_WIN32(ERROR_DS_DST_DOMAIN_NOT_NATIVE); // not an NT5 domain.
|
|
_JumpError(hr, error, "DsGetDcName");
|
|
}
|
|
|
|
// Bind to the target DS
|
|
dwNetStatus=DsBind(
|
|
pDcInfo->DomainControllerName, // DC Address
|
|
NULL, // DNS domain name
|
|
&hDs ); // DS handle
|
|
if (NO_ERROR!=dwNetStatus) {
|
|
hr=HRESULT_FROM_WIN32(dwNetStatus);
|
|
_JumpError(hr, error, "DsBind");
|
|
}
|
|
|
|
// Get the list of DCs from the target DS.
|
|
dwNetStatus=DsGetDomainControllerInfo(
|
|
hDs, // DS handle
|
|
pDcInfo->DomainName, // domain name
|
|
1, // Info level
|
|
&dwDcCount, // number of names returned
|
|
(void **)&rgDsDcInfo); // array of names
|
|
if (NO_ERROR!=dwNetStatus ) {
|
|
hr=HRESULT_FROM_WIN32(dwNetStatus);
|
|
_JumpError(hr, error, "DsGetDomainControllerInfo");
|
|
}
|
|
|
|
// figure out how many DCs there are with DNS names
|
|
nDcs=0;
|
|
for (nIndex=0; nIndex<dwDcCount; nIndex++) {
|
|
if (NULL!=rgDsDcInfo[nIndex].DnsHostName) {
|
|
nDcs++;
|
|
}
|
|
}
|
|
if (nDcs<dwDcCount) {
|
|
DebugWPrintf2(L"Found %u non-DNS DCs out of %u, which will be ignored.\n", dwDcCount-nDcs, dwDcCount);
|
|
}
|
|
if (0==nDcs) {
|
|
hr=HRESULT_FROM_WIN32(ERROR_DOMAIN_CONTROLLER_NOT_FOUND); // no usable DCs
|
|
_JumpError(hr, error, "Search rgDsDcInfo for usable DCs");
|
|
}
|
|
|
|
// allocate the list
|
|
rgDcs=(DcInfo *)LocalAlloc(LPTR, sizeof(DcInfo)*nDcs);
|
|
_JumpIfOutOfMemory(hr, error, rgDcs);
|
|
|
|
// copy the names into it
|
|
nDcIndex=0;
|
|
for (nIndex=0; nIndex<dwDcCount; nIndex++) {
|
|
if (NULL!=rgDsDcInfo[nIndex].DnsHostName) {
|
|
|
|
// allocate and copy name
|
|
|
|
rgDcs[nDcIndex].wszDnsName=(WCHAR *)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(rgDsDcInfo[nIndex].DnsHostName)+1));
|
|
_JumpIfOutOfMemory(hr, error, rgDcs[nDcIndex].wszDnsName);
|
|
wcscpy(rgDcs[nDcIndex].wszDnsName, rgDsDcInfo[nIndex].DnsHostName);
|
|
|
|
//_Verify(NULL!=rgDsDcInfo[nIndex].NetbiosName, hr, error);
|
|
//rgDcs[nDcIndex].wszDnsName=(WCHAR *)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(rgDsDcInfo[nIndex].NetbiosName)+1));
|
|
//_JumpIfOutOfMemory(hr, error, rgDcs[nDcIndex].wszDnsName);
|
|
//wcscpy(rgDcs[nDcIndex].wszDnsName, rgDsDcInfo[nIndex].NetbiosName);
|
|
|
|
// copy PDCness
|
|
rgDcs[nDcIndex].bIsPdc=rgDsDcInfo[nIndex].fIsPdc?true:false;
|
|
nDcIndex++;
|
|
}
|
|
}
|
|
|
|
// move the data to the out parameters
|
|
*prgDcs=rgDcs;
|
|
rgDcs=NULL;
|
|
*pnDcs=nDcs;
|
|
|
|
hr=S_OK;
|
|
|
|
error:
|
|
if (NULL!=rgDcs) {
|
|
for (nIndex=0; nIndex<nDcs; nIndex++) {
|
|
FreeDcInfo(&rgDcs[nIndex]);
|
|
}
|
|
LocalFree(rgDcs);
|
|
}
|
|
if (NULL!=rgDsDcInfo ) {
|
|
DsFreeDomainControllerInfo(1, dwDcCount, rgDsDcInfo);
|
|
}
|
|
if (NULL!=hDs) {
|
|
DsUnBind(&hDs);
|
|
}
|
|
if (NULL!=pDcInfo) {
|
|
NetApiBufferFree(pDcInfo);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
MODULEPRIVATE HRESULT GetDcListFromNetlogon(const WCHAR * wszDomainName, DcInfo ** prgDcs, unsigned int * pnDcs)
|
|
{
|
|
HRESULT hr;
|
|
NET_API_STATUS dwNetStatus;
|
|
DWORD dwEntriesRead;
|
|
DWORD dwTotalEntries;
|
|
unsigned int nIndex;
|
|
unsigned int nDcIndex;
|
|
unsigned int nDcs;
|
|
|
|
// varaibles that must be cleaned up
|
|
DcInfo * rgDcs=NULL;
|
|
SERVER_INFO_101 * rgsiServerInfo=NULL;
|
|
|
|
// initialize out variables
|
|
*prgDcs=NULL;
|
|
*pnDcs=0;
|
|
|
|
// enumerate all PDC and BDCs
|
|
dwNetStatus=NetServerEnum(
|
|
NULL, // server to query
|
|
101, // info level
|
|
(BYTE **)&rgsiServerInfo, // output buffer
|
|
MAX_PREFERRED_LENGTH, // desired return buf size
|
|
&dwEntriesRead, // entries in output buffer
|
|
&dwTotalEntries, // total number of entries available
|
|
SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL, // server type to find
|
|
wszDomainName, // domain to search
|
|
NULL); // reserved
|
|
if (NO_ERROR!=dwNetStatus ) {
|
|
hr=HRESULT_FROM_WIN32(dwNetStatus);
|
|
_JumpError(hr, error, "NetServerEnum");
|
|
}
|
|
|
|
// count how many NT 5 servers there are
|
|
nDcs=0;
|
|
for (nIndex=0; nIndex<dwEntriesRead; nIndex++) {
|
|
if (0!=(rgsiServerInfo[nIndex].sv101_type&SV_TYPE_NT)
|
|
&& rgsiServerInfo[nIndex].sv101_version_major>=5) {
|
|
nDcs++;
|
|
}
|
|
}
|
|
if (nDcs<dwEntriesRead) {
|
|
DebugWPrintf2(L"Found %u non-NT5 DCs out of %u, which will be ignored.\n", dwEntriesRead-nDcs, dwEntriesRead);
|
|
}
|
|
if (0==nDcs) {
|
|
hr=HRESULT_FROM_WIN32(ERROR_DOMAIN_CONTROLLER_NOT_FOUND); // no usable DCs
|
|
_JumpError(hr, error, "Search rgsiServerInfo for usable DCs");
|
|
}
|
|
|
|
// allocate the list
|
|
rgDcs=(DcInfo *)LocalAlloc(LPTR, sizeof(DcInfo)*nDcs);
|
|
_JumpIfOutOfMemory(hr, error, rgDcs);
|
|
|
|
// copy the names into it
|
|
nDcIndex=0;
|
|
for (nIndex=0; nIndex<dwEntriesRead; nIndex++) {
|
|
if (0!=(rgsiServerInfo[nIndex].sv101_type&SV_TYPE_NT)
|
|
&& rgsiServerInfo[nIndex].sv101_version_major>=5) {
|
|
|
|
// allocate and copy name
|
|
rgDcs[nDcIndex].wszDnsName=(WCHAR *)LocalAlloc(LPTR, sizeof(WCHAR)*(wcslen(rgsiServerInfo[nIndex].sv101_name)+1));
|
|
_JumpIfOutOfMemory(hr, error, rgDcs[nDcIndex].wszDnsName);
|
|
wcscpy(rgDcs[nDcIndex].wszDnsName, rgsiServerInfo[nIndex].sv101_name);
|
|
|
|
// copy PDCness
|
|
rgDcs[nDcIndex].bIsPdc=(rgsiServerInfo[nIndex].sv101_type&SV_TYPE_DOMAIN_CTRL)?true:false;
|
|
nDcIndex++;
|
|
}
|
|
}
|
|
|
|
// move the data to the out parameters
|
|
*prgDcs=rgDcs;
|
|
rgDcs=NULL;
|
|
*pnDcs=nDcs;
|
|
|
|
hr=S_OK;
|
|
|
|
error:
|
|
if (NULL!=rgDcs) {
|
|
for (nIndex=0; nIndex<nDcs; nIndex++) {
|
|
FreeDcInfo(&rgDcs[nIndex]);
|
|
}
|
|
LocalFree(rgDcs);
|
|
}
|
|
if (NULL!=rgsiServerInfo) {
|
|
NetApiBufferFree(rgsiServerInfo);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
MODULEPRIVATE HRESULT FillInIpAddresses(DcInfo * pdi) {
|
|
HRESULT hr;
|
|
DWORD dwDataLen;
|
|
unsigned int nIndex;
|
|
|
|
// pointers that must be cleaned up
|
|
HANDLE hSearch=INVALID_HANDLE_VALUE;
|
|
WSAQUERYSETW * pqsResult=NULL;
|
|
in_addr * rgiaLocalIpAddresses=NULL;
|
|
in_addr * rgiaRemoteIpAddresses=NULL;
|
|
|
|
DebugWPrintf1(L"Looking up server \"%s\":\n", pdi->wszDnsName);
|
|
|
|
// initialize the search
|
|
AFPROTOCOLS apInetUdp={AF_INET, IPPROTO_UDP};
|
|
GUID guidNtp=SVCID_NTP_UDP;
|
|
WSAQUERYSETW qsSearch;
|
|
ZeroMemory(&qsSearch, sizeof(qsSearch));
|
|
qsSearch.dwSize=sizeof(qsSearch);
|
|
qsSearch.lpszServiceInstanceName=const_cast<WCHAR *>(pdi->wszDnsName);
|
|
qsSearch.lpServiceClassId=&guidNtp;
|
|
qsSearch.dwNameSpace=NS_ALL;
|
|
qsSearch.dwNumberOfProtocols=1;
|
|
qsSearch.lpafpProtocols=&apInetUdp;
|
|
|
|
// begin the search
|
|
if (SOCKET_ERROR==WSALookupServiceBegin(&qsSearch, LUP_RETURN_ADDR/*flags*/, &hSearch)) {
|
|
hr=HRESULT_FROM_WIN32(WSAGetLastError());
|
|
_JumpError(hr, error, "WSALookupServiceBegin");
|
|
}
|
|
|
|
// get the buffer size for the first result set
|
|
//dwDataLen=1;
|
|
//_Verify(SOCKET_ERROR==WSALookupServiceNext(hSearch, LUP_RETURN_ADDR/*flags*/, &dwDataLen, &qsSearch), hr, error);
|
|
//hr=WSAGetLastError();
|
|
//if (WSAEFAULT!=hr) {
|
|
// hr=HRESULT_FROM_WIN32(hr);
|
|
// _JumpError(hr, error, "WSALookupServiceNext(1)");
|
|
//}
|
|
dwDataLen=5*1024;
|
|
|
|
// allocate the buffer
|
|
pqsResult=(WSAQUERYSETW *)LocalAlloc(LPTR, dwDataLen);
|
|
_JumpIfOutOfMemory(hr, error, pqsResult);
|
|
|
|
// retrieve the result set
|
|
if (SOCKET_ERROR==WSALookupServiceNext(hSearch, LUP_RETURN_ADDR/*flags*/, &dwDataLen, pqsResult)) {
|
|
hr=HRESULT_FROM_WIN32(WSAGetLastError());
|
|
_JumpError(hr, error, "WSALookupServiceNext(2)");
|
|
}
|
|
_Verify(0!=pqsResult->dwNumberOfCsAddrs, hr, error) ;
|
|
|
|
// allocate room for the IP addresses
|
|
rgiaLocalIpAddresses=(in_addr *)LocalAlloc(LPTR, sizeof(in_addr)*pqsResult->dwNumberOfCsAddrs);
|
|
_JumpIfOutOfMemory(hr, error, rgiaLocalIpAddresses);
|
|
rgiaRemoteIpAddresses=(in_addr *)LocalAlloc(LPTR, sizeof(in_addr)*pqsResult->dwNumberOfCsAddrs);
|
|
_JumpIfOutOfMemory(hr, error, rgiaRemoteIpAddresses);
|
|
|
|
// copy the IP addresses
|
|
for (nIndex=0; nIndex<pqsResult->dwNumberOfCsAddrs; nIndex++) {
|
|
// copy local
|
|
_Verify(sizeof(sockaddr)==pqsResult->lpcsaBuffer[nIndex].LocalAddr.iSockaddrLength, hr, error);
|
|
_Verify(AF_INET==pqsResult->lpcsaBuffer[nIndex].LocalAddr.lpSockaddr->sa_family, hr, error);
|
|
rgiaLocalIpAddresses[nIndex].S_un.S_addr=((sockaddr_in *)(pqsResult->lpcsaBuffer[nIndex].LocalAddr.lpSockaddr))->sin_addr.S_un.S_addr;
|
|
// copy remote
|
|
_Verify(sizeof(sockaddr)==pqsResult->lpcsaBuffer[nIndex].RemoteAddr.iSockaddrLength, hr, error);
|
|
_Verify(AF_INET==pqsResult->lpcsaBuffer[nIndex].RemoteAddr.lpSockaddr->sa_family, hr, error);
|
|
rgiaRemoteIpAddresses[nIndex].S_un.S_addr=((sockaddr_in *)(pqsResult->lpcsaBuffer[nIndex].RemoteAddr.lpSockaddr))->sin_addr.S_un.S_addr;
|
|
}
|
|
|
|
// move the data to the out parameters
|
|
pdi->nIpAddresses=pqsResult->dwNumberOfCsAddrs;
|
|
pdi->rgiaLocalIpAddresses=rgiaLocalIpAddresses;
|
|
rgiaLocalIpAddresses=NULL;
|
|
pdi->rgiaRemoteIpAddresses=rgiaRemoteIpAddresses;
|
|
rgiaRemoteIpAddresses=NULL;
|
|
|
|
hr=S_OK;
|
|
|
|
error:
|
|
if (NULL!=rgiaLocalIpAddresses) {
|
|
LocalFree(rgiaLocalIpAddresses);
|
|
}
|
|
if (NULL!=rgiaRemoteIpAddresses) {
|
|
LocalFree(rgiaRemoteIpAddresses);
|
|
}
|
|
if (NULL!=pqsResult) {
|
|
LocalFree(pqsResult);
|
|
}
|
|
if (INVALID_HANDLE_VALUE!=hSearch) {
|
|
if (SOCKET_ERROR==WSALookupServiceEnd(hSearch)) {
|
|
HRESULT hr2=HRESULT_FROM_WIN32(WSAGetLastError());
|
|
_IgnoreError(hr2, "WSALookupServiceEnd");
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//####################################################################
|
|
// Globals
|
|
|
|
//--------------------------------------------------------------------
|
|
void FreeDcInfo(DcInfo * pdci) {
|
|
if (NULL!=pdci->wszDnsName) {
|
|
LocalFree(pdci->wszDnsName);
|
|
}
|
|
if (NULL!=pdci->rgiaLocalIpAddresses) {
|
|
LocalFree(pdci->rgiaLocalIpAddresses);
|
|
}
|
|
if (NULL!=pdci->rgiaRemoteIpAddresses) {
|
|
LocalFree(pdci->rgiaRemoteIpAddresses);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// Get a list of DCs in this domain
|
|
HRESULT GetDcList(const WCHAR * wszDomainName, bool bGetIps, DcInfo ** prgDcs, unsigned int * pnDcs)
|
|
{
|
|
HRESULT hr;
|
|
unsigned int nDcs;
|
|
unsigned int nIndex;
|
|
|
|
// varaibles that must be cleaned up
|
|
DcInfo * rgDcs=NULL;
|
|
|
|
// initialize out variables
|
|
*prgDcs=NULL;
|
|
*pnDcs=0;
|
|
|
|
hr=GetDcListFromDs(wszDomainName, &rgDcs, &nDcs);
|
|
if (FAILED(hr)) {
|
|
_IgnoreError(hr, "GetDcListFromDs");
|
|
hr=GetDcListFromNetlogon(wszDomainName, &rgDcs, &nDcs);
|
|
_JumpIfError(hr, error, "GetDcListFromNetlogon");
|
|
}
|
|
|
|
if (bGetIps) {
|
|
// get the info about the DCs
|
|
for (nIndex=0; nIndex<nDcs; nIndex++) {
|
|
hr=FillInIpAddresses(&rgDcs[nIndex]);
|
|
if (FAILED(hr)) {
|
|
_IgnoreError(hr, "FillInIpAddresses");
|
|
if (nIndex!=nDcs-1) {
|
|
// swap it with the last one
|
|
WCHAR * wszDnsName=rgDcs[nIndex].wszDnsName;
|
|
rgDcs[nIndex].wszDnsName=rgDcs[nDcs-1].wszDnsName;
|
|
rgDcs[nDcs-1].wszDnsName=wszDnsName;
|
|
|
|
in_addr * rgiaLocalIpAddresses=rgDcs[nIndex].rgiaLocalIpAddresses;
|
|
rgDcs[nIndex].rgiaLocalIpAddresses=rgDcs[nDcs-1].rgiaLocalIpAddresses;
|
|
rgDcs[nDcs-1].rgiaLocalIpAddresses=rgiaLocalIpAddresses;
|
|
|
|
in_addr * rgiaRemoteIpAddresses=rgDcs[nIndex].rgiaRemoteIpAddresses;
|
|
rgDcs[nIndex].rgiaRemoteIpAddresses=rgDcs[nDcs-1].rgiaRemoteIpAddresses;
|
|
rgDcs[nDcs-1].rgiaRemoteIpAddresses=rgiaRemoteIpAddresses;
|
|
|
|
// non-pointers can just be copied
|
|
rgDcs[nIndex].nIpAddresses=rgDcs[nDcs-1].nIpAddresses;
|
|
rgDcs[nIndex].bIsPdc=rgDcs[nDcs-1].bIsPdc;
|
|
rgDcs[nIndex].bIsGoodTimeSource=rgDcs[nDcs-1].bIsGoodTimeSource;
|
|
}
|
|
DebugWPrintf1(L"Dropping '%s' because we cannot get an IP address.\n", rgDcs[nDcs-1].wszDnsName);
|
|
nDcs--;
|
|
nIndex--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (0==nDcs) {
|
|
hr=HRESULT_FROM_WIN32(ERROR_DOMAIN_CONTROLLER_NOT_FOUND); // no usable DCs
|
|
_JumpError(hr, error, "Getting IP address for at least one DC");
|
|
}
|
|
|
|
// move the data to the out parameters
|
|
*prgDcs=rgDcs;
|
|
rgDcs=NULL;
|
|
*pnDcs=nDcs;
|
|
|
|
hr=S_OK;
|
|
|
|
error:
|
|
if (NULL!=rgDcs) {
|
|
for (nIndex=0; nIndex<nDcs; nIndex++) {
|
|
FreeDcInfo(&rgDcs[nIndex]);
|
|
}
|
|
LocalFree(rgDcs);
|
|
}
|
|
return hr;
|
|
} |