windows-nt/Source/XPSP1/NT/shell/ext/dsui/dsuiext/domain.cpp
2020-09-26 16:20:57 +08:00

1001 lines
31 KiB
C++

#include "pch.h"
#include "lm.h" // NET_API_STATUS
#include <dsgetdc.h> // DsEnumerateDomainTrusts
#include <subauth.h>
#include <ntlsa.h> // TRUST_TYPE_XXX
#pragma hdrstop
/*-----------------------------------------------------------------------------
/ Misc data
/----------------------------------------------------------------------------*/
//
// Globally cached domain list, this is cached an free'd as required
//
PDOMAIN_TREE g_pDomainTree = NULL;
DWORD g_dwFlags = 0;
//
// CDsBrowseDomainTree
//
class CDsDomainTreeBrowser : public IDsBrowseDomainTree
{
private:
STDMETHODIMP _GetDomains(PDOMAIN_TREE *ppDomainTree, DWORD dwFlags);
LONG _cRef;
LPWSTR _pComputerName;
LPWSTR _pUserName;
LPWSTR _pPassword;
LPDOMAINTREE _pDomainTree;
DWORD _dwFlags;
public:
CDsDomainTreeBrowser();
~CDsDomainTreeBrowser();
// IUnknown members
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHODIMP QueryInterface(REFIID riid, LPVOID FAR* ppvObject);
// IDsBrowseDomainTree
STDMETHODIMP BrowseTo(HWND hwndParent, LPWSTR *ppszTargetPath, DWORD dwFlags);
STDMETHODIMP GetDomains(PDOMAIN_TREE *ppDomainTree, DWORD dwFlags);
STDMETHODIMP FreeDomains(PDOMAIN_TREE* ppDomainTree);
STDMETHODIMP FlushCachedDomains();
STDMETHODIMP SetComputer(LPCWSTR pComputerName, LPCWSTR pUserName, LPCWSTR pPassword);
};
CDsDomainTreeBrowser::CDsDomainTreeBrowser() :
_cRef(1),
_pComputerName(NULL),
_pUserName(NULL),
_pPassword(NULL),
_pDomainTree(NULL),
_dwFlags(0)
{
DllAddRef();
}
CDsDomainTreeBrowser::~CDsDomainTreeBrowser()
{
FreeDomains(&_pDomainTree);
LocalFreeStringW(&_pComputerName);
LocalFreeStringW(&_pUserName);
LocalFreeStringW(&_pPassword);
DllRelease();
}
// IUnknown
ULONG CDsDomainTreeBrowser::AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG CDsDomainTreeBrowser::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
HRESULT CDsDomainTreeBrowser::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CDsDomainTreeBrowser, IDsBrowseDomainTree), // IID_IID_IDsBrowseDomainTree
{0, 0 },
};
return QISearch(this, qit, riid, ppv);
}
//
// handle create instance
//
STDAPI CDsDomainTreeBrowser_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
CDsDomainTreeBrowser *pddtb = new CDsDomainTreeBrowser();
if ( !pddtb )
return E_OUTOFMEMORY;
HRESULT hres = pddtb->QueryInterface(IID_IUnknown, (void **)ppunk);
pddtb->Release();
return hres;
}
//---------------------------------------------------------------------------//
// IDsBrowseDomainTree
//---------------------------------------------------------------------------//
STDMETHODIMP CDsDomainTreeBrowser::SetComputer(LPCWSTR pComputerName, LPCWSTR pUserName, LPCWSTR pPassword)
{
HRESULT hres;
TraceEnter(TRACE_DOMAIN, "CDsDomainTreeBrowser::SetComputer");
LocalFreeStringW(&_pComputerName);
LocalFreeStringW(&_pUserName);
LocalFreeStringW(&_pPassword);
hres = LocalAllocStringW(&_pComputerName, pComputerName);
if ( SUCCEEDED(hres) )
hres = LocalAllocStringW(&_pUserName, pUserName);
if ( SUCCEEDED(hres) )
hres = LocalAllocStringW(&_pPassword, pPassword);
if ( FAILED(hres) )
{
LocalFreeStringW(&_pComputerName);
LocalFreeStringW(&_pUserName);
LocalFreeStringW(&_pPassword);
}
TraceLeaveResult(hres);
}
//---------------------------------------------------------------------------//
#define BROWSE_CTX_HELP_FILE _T("dsadmin.hlp")
#define IDH_DOMAIN_TREE 300000800
const DWORD aBrowseHelpIDs[] =
{
IDC_DOMAIN_TREE,IDH_DOMAIN_TREE,
0, 0
};
struct DIALOG_STUFF
{
LPWSTR pszName; // domain name (if no dns, use netbios)
LPWSTR pszNCName; // FQDN
PDOMAIN_TREE pDomains;
};
//
// recursive tree filling stuff
//
HTREEITEM
_AddOneItem( HTREEITEM hParent, LPWSTR szText, HTREEITEM hInsAfter,
int iImage, int cChildren, HWND hwndTree, LPARAM Domain)
{
HTREEITEM hItem;
TV_ITEM tvI = { 0 };
TV_INSERTSTRUCT tvIns = { 0 };
USES_CONVERSION;
// The .pszText, .iImage, and .iSelectedImage are filled in.
tvI.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
tvI.pszText = W2T(szText);
tvI.cchTextMax = lstrlen(tvI.pszText);
tvI.iImage = iImage;
tvI.iSelectedImage = iImage;
tvI.cChildren = cChildren;
tvI.lParam = Domain;
tvIns.item = tvI;
tvIns.hInsertAfter = hInsAfter;
tvIns.hParent = hParent;
return TreeView_InsertItem(hwndTree, &tvIns);;
}
void
_AddChildren(DOMAIN_DESC *pDomain, HWND hTree, HTREEITEM hParent)
{
DOMAIN_DESC * pChild = pDomain->pdChildList;
for ( pChild = pDomain->pdChildList ; pChild ; pChild = pChild->pdNextSibling )
{
HTREEITEM hThis = _AddOneItem (hParent, pChild->pszName, TVI_SORT, 0,
(pChild->pdChildList ? 1 : 0), hTree, (LPARAM)pChild);
if (pChild->pdChildList != NULL)
_AddChildren (pChild, hTree, hThis);
}
}
//
// DlgProc for the simple browser
//
INT_PTR CALLBACK
_BrowserDlgProc (HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
HWND hTree = GetDlgItem (hwnd, IDC_DOMAIN_TREE);
DIALOG_STUFF *pDialogInfo = (DIALOG_STUFF *)GetWindowLongPtr(hwnd, DWLP_USER);
switch (Msg)
{
case WM_INITDIALOG:
{
pDialogInfo = (DIALOG_STUFF *)lParam;
PDOMAIN_TREE pDomains = pDialogInfo->pDomains;
SetWindowLongPtr(hwnd, DWLP_USER, lParam);
// assume all images are the same for the tree view so load it and set accordingly
CLASSCACHEENTRY *pcce = NULL;
CLASSCACHEGETINFO ccgi = { 0 };
HICON hIcon = NULL;
ccgi.dwFlags = CLASSCACHE_ICONS;
ccgi.pObjectClass = pDomains->aDomains[0].pszObjectClass;
// should be pasing computer name to get correct display specifier
// ccgi.pServer = _pComputerName;
if ( SUCCEEDED(ClassCache_GetClassInfo(&ccgi, &pcce)) )
{
WCHAR szBuffer[MAX_PATH];
INT resid;
HRESULT hres = _GetIconLocation(pcce, DSGIF_GETDEFAULTICON, szBuffer, ARRAYSIZE(szBuffer), &resid);
ClassCache_ReleaseClassInfo(&pcce);
if ( hres == S_OK )
{
HICON hIcon;
INT cxSmIcon = GetSystemMetrics(SM_CXSMICON);
INT cySmIcon = GetSystemMetrics(SM_CYSMICON);
if ( 1 == PrivateExtractIcons(szBuffer, resid, cxSmIcon, cySmIcon, &hIcon, NULL, 1, LR_LOADFROMFILE) )
{
HIMAGELIST himl = ImageList_Create(cxSmIcon, cySmIcon, ILC_COLOR|ILC_MASK, 0, 1);
if ( himl )
{
ImageList_AddIcon(himl, hIcon);
TreeView_SetImageList(hTree, himl, TVSIL_NORMAL);
}
DestroyIcon(hIcon);
}
}
}
// now populate the tree with the items in the domain structure
for (PDOMAIN_DESC pRootDomain = pDomains->aDomains; pRootDomain; pRootDomain = pRootDomain->pdNextSibling)
{
HTREEITEM hRoot = _AddOneItem(TVI_ROOT, pRootDomain->pszName, TVI_SORT, 0,
(pRootDomain->pdChildList ? 1 : 0), hTree, (LPARAM) pRootDomain);
if (pRootDomain->pdChildList != NULL)
_AddChildren(pRootDomain, hTree, hRoot);
}
return TRUE;
}
case WM_HELP:
{
WinHelp((HWND)(((LPHELPINFO)lParam)->hItemHandle),
BROWSE_CTX_HELP_FILE,
HELP_WM_HELP,
(DWORD_PTR)(PVOID)aBrowseHelpIDs);
return TRUE;
}
case WM_CONTEXTMENU:
{
WinHelp((HWND)wParam,
BROWSE_CTX_HELP_FILE,
HELP_CONTEXTMENU,
(DWORD_PTR)(PVOID)aBrowseHelpIDs);
return TRUE;
}
case WM_NOTIFY:
{
NMHDR* pnmhdr = (NMHDR*)lParam;
if (IDC_DOMAIN_TREE != pnmhdr->idFrom || NM_DBLCLK != pnmhdr->code)
return TRUE;
TV_ITEM tvi;
tvi.hItem = TreeView_GetSelection(hTree);
tvi.mask = TVIF_CHILDREN;
if ( TreeView_GetItem(hTree, &tvi) == TRUE )
{
if (tvi.cChildren == 0)
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDOK, (WORD)0), (LPARAM)0);
}
return TRUE;
}
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDOK:
{
TV_ITEM tvi;
tvi.hItem = TreeView_GetSelection(hTree);
tvi.mask = TVIF_PARAM;
if ( TreeView_GetItem(hTree, &tvi) == TRUE )
{
DOMAIN_DESC *pDomain = (DOMAIN_DESC *)tvi.lParam;
pDialogInfo->pszName = pDomain->pszName;
pDialogInfo->pszNCName = pDomain->pszNCName;
EndDialog (hwnd, TRUE);
}
else
{
pDialogInfo->pszName = NULL;
pDialogInfo->pszNCName = NULL;
EndDialog (hwnd, FALSE);
}
return TRUE;
}
case IDCANCEL:
{
pDialogInfo->pszName = NULL;
pDialogInfo->pszNCName = NULL;
EndDialog (hwnd, FALSE);
return TRUE;
}
}
}
}
return FALSE;
}
//
// exposed API for browsing the tree
//
STDMETHODIMP CDsDomainTreeBrowser::BrowseTo(HWND hwndParent, LPWSTR *ppszTargetPath, DWORD dwFlags)
{
if (!ppszTargetPath)
return E_INVALIDARG;
HRESULT hr;
PDOMAIN_TREE pDomainTree = NULL;
DIALOG_STUFF DlgInfo;
*ppszTargetPath = NULL; // result is NULL
hr = GetDomains(&pDomainTree, dwFlags);
if (SUCCEEDED(hr))
{
DlgInfo.pDomains = pDomainTree;
DWORD res = (DWORD)DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_DOMAINBROWSER),
hwndParent, _BrowserDlgProc, (LPARAM)&DlgInfo);
if (res == IDOK)
{
LPWSTR pszPath = DlgInfo.pszName;
if (dwFlags & DBDTF_RETURNFQDN)
pszPath = DlgInfo.pszNCName;
if (pszPath)
{
*ppszTargetPath = (LPWSTR)CoTaskMemAlloc(StringByteSizeW(pszPath));
if (!*ppszTargetPath)
hr = E_OUTOFMEMORY;
else
StrCpyW(*ppszTargetPath, pszPath);
} else
{
hr = S_FALSE;
}
}
else
{
hr = S_FALSE;
}
}
FreeDomains(&pDomainTree);
return hr;
}
//---------------------------------------------------------------------------//
// keep using old values for win9x
// the following comments are for nt when using new api
struct DOMAIN_DATA
{
WCHAR szName[MAX_PATH]; // domain name (if no dns, use netbios)
WCHAR szPath[MAX_PATH]; // set to blank
WCHAR szTrustParent[MAX_PATH]; // parent domain name (if no dns, use netbios)
WCHAR szNCName[MAX_PATH]; // FQDN: DC=mydomain,DC=microsoft,DC=com
BOOL fConnected;
BOOL fRoot; // true if root
ULONG ulFlags; // type of domain, e.g., external trusted domain
BOOL fDownLevel; // if NT4 domain
DOMAIN_DATA * pNext;
};
#define FIX_UP(cast, p, pOriginal, pNew) p ? ((cast)(((LPBYTE)p-(LPBYTE)pOriginal)+(LPBYTE)pNew)):NULL
#define DOMAIN_OBJECT_CLASS L"domainDNS" // fixed class for domain.
STDMETHODIMP CDsDomainTreeBrowser::GetDomains(PDOMAIN_TREE *ppDomainTree, DWORD dwFlags)
{
HRESULT hr;
LPDOMAINTREE pDomainTree = NULL;
LPDOMAINTREE pSrcDomainTree = NULL;
LPDOMAINDESC pDomainDesc = NULL;
DWORD i;
TraceEnter(TRACE_DOMAIN, "CDsDomainTreeBrowser::GetDomains");
if ( !ppDomainTree )
ExitGracefully(hr, E_INVALIDARG, "ppDomainTree == NULL");
*ppDomainTree = NULL;
// we support the user giving us a search root (::SetSearchRoot) so if we have
// one then lets cache in this object the domain tree, otherwise fall back
// to the global one.
if ( _pComputerName )
{
TraceMsg("We have a computer name, so checking instance cached object");
if ( !_pDomainTree || _dwFlags != dwFlags)
{
TraceMsg("Caching instance domain list");
if (_pDomainTree)
FreeDomains(&_pDomainTree);
hr = _GetDomains(&_pDomainTree, dwFlags);
FailGracefully(hr, "Failed to get cached domain list");
_dwFlags = dwFlags;
}
pSrcDomainTree = _pDomainTree;
}
else
{
TraceMsg("Checking globally cached domain tree (no search root)");
if ( !g_pDomainTree || g_dwFlags != dwFlags)
{
TraceMsg("Caching global domain list");
if (g_pDomainTree)
FreeDomains(&g_pDomainTree);
hr = _GetDomains(&g_pDomainTree, dwFlags);
FailGracefully(hr, "Failed to get cached domain list");
g_dwFlags = dwFlags;
}
pSrcDomainTree = g_pDomainTree;
}
if ( !pSrcDomainTree )
ExitGracefully(hr, E_FAIL, "Failed to get cached tree");
// move and relocate the domain tree, walk all the pointers and offset
// them from the original to the new.
TraceMsg("Allocating buffer to copy the domain list");
pDomainTree = (LPDOMAINTREE)CoTaskMemAlloc(pSrcDomainTree->dsSize);
TraceAssert(pDomainTree);
if ( !pDomainTree )
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate copy of the domain tree");
memcpy(pDomainTree, pSrcDomainTree, pSrcDomainTree->dsSize);
Trace(TEXT("Fixing up %d domains"), pDomainTree->dwCount);
for ( i = 0 ; i != pDomainTree->dwCount ; i++ )
{
pDomainTree->aDomains[i].pszName = FIX_UP(LPWSTR, pDomainTree->aDomains[i].pszName, pSrcDomainTree, pDomainTree);
pDomainTree->aDomains[i].pszPath = FIX_UP(LPWSTR, pDomainTree->aDomains[i].pszPath, pSrcDomainTree, pDomainTree);
pDomainTree->aDomains[i].pszNCName = FIX_UP(LPWSTR, pDomainTree->aDomains[i].pszNCName, pSrcDomainTree, pDomainTree);
pDomainTree->aDomains[i].pszTrustParent = FIX_UP(LPWSTR, pDomainTree->aDomains[i].pszTrustParent, pSrcDomainTree, pDomainTree);
pDomainTree->aDomains[i].pszObjectClass = FIX_UP(LPWSTR, pDomainTree->aDomains[i].pszObjectClass, pSrcDomainTree, pDomainTree);
pDomainTree->aDomains[i].pdChildList = FIX_UP(LPDOMAINDESC, pDomainTree->aDomains[i].pdChildList, pSrcDomainTree, pDomainTree);
pDomainTree->aDomains[i].pdNextSibling = FIX_UP(LPDOMAINDESC, pDomainTree->aDomains[i].pdNextSibling, pSrcDomainTree, pDomainTree);
}
*ppDomainTree = pDomainTree;
hr = S_OK;
exit_gracefully:
if ( FAILED(hr) )
CoTaskMemFree(pDomainTree);
TraceLeaveResult(hr);
}
//
// Real _GetDomains that does the work of finding the trusted domains
//
STDMETHODIMP CDsDomainTreeBrowser::_GetDomains(PDOMAIN_TREE *ppDomainTree, DWORD dwFlags)
{
HRESULT hr = S_OK;
UINT cbSize = 0;
UINT cDomains = 0, cRootDomains =0, cbStringStorage = 0;
struct DOMAIN_DATA * pCurrentDomain = NULL;
struct DOMAIN_DATA * pFirstDomain = NULL;
DOMAIN_DESC * pDestDomain = NULL;
DOMAIN_DESC * pDestRootDomain = NULL;
LPWSTR pNextFree;
UINT index, index_inner;
DOMAIN_DESC * pPotentialChild, * pPotentialParent;
USES_CONVERSION;
ULONG ulParentIndex = 0;
ULONG ulCurrentIndex = 0;
ULONG ulEntryCount = 0;
PDS_DOMAIN_TRUSTS pDomainList = NULL;
PDS_DOMAIN_TRUSTS pDomain = NULL;
NET_API_STATUS NetStatus = NO_ERROR;
ULONG ulFlags = DS_DOMAIN_PRIMARY | DS_DOMAIN_IN_FOREST;
BOOL bDownLevelTrust = FALSE;
BOOL bUpLevelTrust = FALSE;
BOOL bExternalTrust = FALSE;
TraceEnter(TRACE_DOMAIN, "CDsDomainTreeBrowser::_GetDomains");
*ppDomainTree = NULL;
if (dwFlags & DBDTF_RETURNINOUTBOUND)
{
ulFlags |= (DS_DOMAIN_DIRECT_INBOUND | DS_DOMAIN_DIRECT_OUTBOUND);
}
else if (dwFlags & DBDTF_RETURNINBOUND)
{
ulFlags |= DS_DOMAIN_DIRECT_INBOUND;
}
else
{
ulFlags |= DS_DOMAIN_DIRECT_OUTBOUND;
}
// wack off the port number if we have server:<n> specified
LPWSTR pszPort = NULL;
if (NULL != _pComputerName)
{
pszPort = StrChrW(_pComputerName, L':');
if ( pszPort )
*pszPort = L'\0';
}
// get the domain list
NetStatus = DsEnumerateDomainTrusts(
_pComputerName,
ulFlags,
&pDomainList,
&ulEntryCount );
if (ERROR_ACCESS_DENIED == NetStatus &&
_pComputerName && *_pComputerName &&
_pUserName && *_pUserName)
{
//
// make the connection, try one more time
//
WCHAR wszIPC[MAX_PATH];
if (L'\\' == *_pComputerName)
{
StrCpyW(wszIPC, _pComputerName);
}
else
{
StrCpyW(wszIPC, L"\\\\");
StrCatW(wszIPC, _pComputerName);
}
StrCatW(wszIPC, L"\\IPC$");
NETRESOURCEW nr = {0};
nr.dwType = RESOURCETYPE_ANY;
nr.lpLocalName = NULL;
nr.lpRemoteName = wszIPC;
nr.lpProvider = NULL;
DWORD dwErr = WNetAddConnection2W(&nr, _pPassword, _pUserName, 0);
if (NO_ERROR == dwErr || ERROR_SESSION_CREDENTIAL_CONFLICT == dwErr)
{
NetStatus = DsEnumerateDomainTrusts(
_pComputerName,
ulFlags,
&pDomainList,
&ulEntryCount );
} else
{
NetStatus = dwErr;
}
//
// soft close the connection opened by us
//
if (NO_ERROR == dwErr)
{
(void) WNetCancelConnection2W(wszIPC, 0, FALSE);
}
}
// restore the port seperator
if ( pszPort )
*pszPort = L':';
if ( NetStatus != NO_ERROR )
ExitGracefully(hr, HRESULT_FROM_WIN32(NetStatus), "Failed to enum trusted domains");
for (ulCurrentIndex=0; ulCurrentIndex<ulEntryCount; ulCurrentIndex++ )
{
pDomain = &(pDomainList[ulCurrentIndex]);
bDownLevelTrust = pDomain->TrustType & TRUST_TYPE_DOWNLEVEL;
bUpLevelTrust = pDomain->TrustType & TRUST_TYPE_UPLEVEL; // trust between 2 NT5 domains
//
// we don't consider other type of trusts, e.g, MIT
//
if (!bDownLevelTrust && !bUpLevelTrust)
continue;
//
// skip if caller has no interest in downlevel trust
//
if ( !(dwFlags & DBDTF_RETURNMIXEDDOMAINS) && bDownLevelTrust)
continue;
bExternalTrust = !(pDomain->Flags & DS_DOMAIN_IN_FOREST);
//
// skip if caller has no interest in external trust
//
if ( !(dwFlags & DBDTF_RETURNEXTERNAL) && bExternalTrust)
continue;
cDomains++;
if (pFirstDomain == NULL)
{
pCurrentDomain = new DOMAIN_DATA;
TraceAssert(pCurrentDomain);
if ( !pCurrentDomain )
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate DOMAIN_DATA structure");
ZeroMemory(pCurrentDomain, sizeof(DOMAIN_DATA));
pFirstDomain = pCurrentDomain;
}
else
{
pCurrentDomain->pNext = new DOMAIN_DATA;
TraceAssert(pCurrentDomain->pNext);
if ( !pCurrentDomain->pNext )
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate DOMAIN_DATA structure (not first item)");
pCurrentDomain = pCurrentDomain->pNext;
ZeroMemory(pCurrentDomain, sizeof(DOMAIN_DATA));
}
// fill the structure with data from the queried object.
pCurrentDomain->pNext = NULL;
pCurrentDomain->ulFlags = pDomain->Flags;
pCurrentDomain->szPath[0] = L'\0';
pCurrentDomain->fDownLevel = bDownLevelTrust;
if (pDomain->DnsDomainName)
{
StrCpyW(pCurrentDomain->szName,
pDomain->DnsDomainName);
// remove the last dot
int i = 0;
PWSTR p = NULL;
int nLength = lstrlenW(pCurrentDomain->szName);
if ( L'.' == pCurrentDomain->szName[nLength-1] )
{
pCurrentDomain->szName[nLength-1] = L'\0';
nLength--;
}
if (dwFlags & DBDTF_RETURNFQDN)
{
// if switch to DsCrackName in the future,
// 1. append trailing '/' to the dns domain name
// 2. use DS_NAME_NO_FLAGS as flags
// 3. use DS_CANONICAL_NAME as formatOffered
// 4. use DS_FQDN_1779_NAME as formatDesired
// what is hDS???
StrCpyW(pCurrentDomain->szNCName, L"DC=");
p = pCurrentDomain->szNCName + 3;
for (i=0; i<nLength; i++)
{
if ( L'.' == pCurrentDomain->szName[i] )
{
StrCpyW(p, L",DC=");
p += 4;
}
else
{
*p = pCurrentDomain->szName[i];
p++;
}
}
}
else
{
pCurrentDomain->szNCName[0] = L'\0';
}
}
else
{
StrCpyW(pCurrentDomain->szName,
pDomain->NetbiosDomainName);
pCurrentDomain->szNCName[0] = L'\0'; // downlevel domain has no FQDN
}
// treat external trusted domain as root domain
pCurrentDomain->fRoot =
( ( !bExternalTrust && (pDomain->Flags & DS_DOMAIN_TREE_ROOT) ) ||
bExternalTrust );
if ( pCurrentDomain->fRoot )
{
cRootDomains++;
} else {
ulParentIndex = pDomain->ParentIndex;
if (pDomainList[ulParentIndex].DnsDomainName)
StrCpyW(pCurrentDomain->szTrustParent, pDomainList[ulParentIndex].DnsDomainName);
else
StrCpyW(pCurrentDomain->szTrustParent, pDomainList[ulParentIndex].NetbiosDomainName);
}
cbStringStorage += StringByteSizeW(pCurrentDomain->szName);
cbStringStorage += StringByteSizeW(pCurrentDomain->szPath);
cbStringStorage += StringByteSizeW(pCurrentDomain->szTrustParent);
cbStringStorage += StringByteSizeW(pCurrentDomain->szNCName);
// hard-coded domainDNS should get from object
cbStringStorage += StringByteSizeW(DOMAIN_OBJECT_CLASS);
}
Trace(TEXT("cDomains %d, cRootDomains %d"), cDomains, cRootDomains);
if ( cRootDomains == 0 )
ExitGracefully(hr, HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO), "No root domains, so failing _GetDomains call");
TraceMsg("Building structure information");
// REVIEW_MARCOC: we allocate more memory than strictly necessary...
cbSize = sizeof(DOMAIN_TREE) + (cDomains * sizeof(DOMAIN_DESC)) + cbStringStorage;
*ppDomainTree = (PDOMAIN_TREE)CoTaskMemAlloc(cbSize);
TraceAssert(*ppDomainTree);
if ( !*ppDomainTree )
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate DOMAINDTREE structure");
memset(*ppDomainTree, 0, cbSize);
pNextFree = (LPWSTR)ByteOffset((*ppDomainTree), sizeof(DOMAIN_TREE) + (cDomains * sizeof(DOMAIN_DESC)) );
// loop to copy the nodes, roots first
pDestRootDomain = &((*ppDomainTree)->aDomains[0]);
pDestDomain = &((*ppDomainTree)->aDomains[cRootDomains]);
for ( pCurrentDomain = pFirstDomain; pCurrentDomain; pCurrentDomain = pCurrentDomain->pNext )
{
if (pCurrentDomain->fRoot)
{
Trace(TEXT("Object is a domain root: %s"), pCurrentDomain->szName);
pDestRootDomain->pszName = pNextFree;
StrCpyW(pDestRootDomain->pszName, pCurrentDomain->szName);
pNextFree += lstrlenW(pCurrentDomain->szName) + 1;
pDestRootDomain->pszPath = pNextFree;
StrCpyW(pDestRootDomain->pszPath, pCurrentDomain->szPath);
pNextFree += lstrlenW(pCurrentDomain->szPath) + 1;
pDestRootDomain->pszNCName = pNextFree;
StrCpyW(pDestRootDomain->pszNCName, pCurrentDomain->szNCName);
pNextFree += lstrlenW(pCurrentDomain->szNCName) + 1;
pDestRootDomain->pszTrustParent = NULL;
// hard-coded domainDNS should get from object
pDestRootDomain->pszObjectClass = pNextFree;
StrCpyW(pDestRootDomain->pszObjectClass, DOMAIN_OBJECT_CLASS);
pNextFree += lstrlenW(DOMAIN_OBJECT_CLASS) + 1;
pDestRootDomain->ulFlags = pCurrentDomain->ulFlags;
pDestRootDomain->fDownLevel = pCurrentDomain->fDownLevel;
pDestRootDomain->pdNextSibling = NULL;
if (pDestRootDomain > &((*ppDomainTree)->aDomains[0]))
{
(&(pDestRootDomain[-1]))->pdNextSibling = pDestRootDomain;
}
pDestRootDomain++;
}
else
{
Trace(TEXT("Object is not a domain root: %s"), pCurrentDomain->szName);
pDestDomain->pszName = pNextFree;
StrCpyW(pDestDomain->pszName, pCurrentDomain->szName);
pNextFree += lstrlenW(pDestDomain->pszName) + 1;
pDestDomain->pszPath = pNextFree;
StrCpyW(pDestDomain->pszPath, pCurrentDomain->szPath);
pNextFree += lstrlenW(pDestDomain->pszPath) + 1;
pDestDomain->pszNCName = pNextFree;
StrCpyW(pDestDomain->pszNCName, pCurrentDomain->szNCName);
pNextFree += lstrlenW(pDestDomain->pszNCName) + 1;
pDestDomain->pszTrustParent = pNextFree;
StrCpyW(pDestDomain->pszTrustParent, pCurrentDomain->szTrustParent);
pNextFree += lstrlenW(pDestDomain->pszTrustParent) + 1;
// hard-coded domainDNS should get from object
pDestDomain->pszObjectClass = pNextFree;
StrCpyW(pDestDomain->pszObjectClass, DOMAIN_OBJECT_CLASS);
pNextFree += lstrlenW(DOMAIN_OBJECT_CLASS) + 1;
pDestDomain->ulFlags = pCurrentDomain->ulFlags;
pDestDomain->fDownLevel = pCurrentDomain->fDownLevel;
pDestDomain++;
}
}
TraceMsg("Finished first pass creating domain structure, now building per level items");
// walk list, picking up each item per level, until all items
// have been placed in structure.
// return structure.
for (index = 0; index < cDomains; index ++)
{
pPotentialParent = &((*ppDomainTree)->aDomains[index]);
Trace(TEXT("pPotentialParent %08x, index %d"), pPotentialParent, index);
for (index_inner = 0; index_inner < cDomains; index_inner++)
{
pPotentialChild = &((*ppDomainTree)->aDomains[index_inner]);
Trace(TEXT("pPotentialChild %08x, index_inner %d"), pPotentialChild, index_inner);
if (pPotentialChild == pPotentialParent)
{
TraceMsg("parent == child, skipping");
continue;
}
Trace(TEXT("Comparing %s to %s"),
pPotentialChild->pszTrustParent ? W2T(pPotentialChild->pszTrustParent):TEXT("NULL"),
W2T(pPotentialParent->pszPath));
if ((pPotentialChild->pszTrustParent != NULL) &&
(!StrCmpW(pPotentialChild->pszTrustParent, pPotentialParent->pszName)))
{
TraceMsg("Child found, scanning for end of child list");
// this is a child. figure out where end of child chain is
if (pPotentialParent->pdChildList == NULL)
{
TraceMsg("Parent has no children, this becomes the child");
pPotentialParent->pdChildList = pPotentialChild;
}
else
{
DOMAIN_DESC * pdScan = pPotentialParent->pdChildList;
Trace(TEXT("Scanning from %08x"), pdScan);
while (pdScan->pdNextSibling != NULL)
{
pdScan = pdScan->pdNextSibling;
Trace(TEXT("Advancing to %08x"), pdScan);
}
Trace(TEXT("Setting next sibling on %08x"), pdScan);
pdScan->pdNextSibling = pPotentialChild;
}
}
}
}
TraceMsg("Finished fix up, setting cbSize + domains");
(*ppDomainTree)->dwCount = cDomains;
(*ppDomainTree)->dsSize = cbSize;
hr = S_OK; // success
exit_gracefully:
if (pDomainList)
NetApiBufferFree(pDomainList);
if (pFirstDomain != NULL)
{
TraceMsg("pFirstDomain != NULL");
while (pFirstDomain != NULL)
{
Trace(TEXT("Releasing domain %08x"), pFirstDomain);
pCurrentDomain = pFirstDomain;
pFirstDomain = pFirstDomain->pNext;
delete pCurrentDomain;
}
}
if ( FAILED(hr) )
{
TraceMsg("Freeing the domain tree structure because we failed");
FreeDomains(ppDomainTree);
}
TraceLeaveResult(hr);
}
//---------------------------------------------------------------------------//
STDMETHODIMP CDsDomainTreeBrowser::FreeDomains(PDOMAIN_TREE* ppDomainTree)
{
HRESULT hr;
TraceEnter(TRACE_DOMAIN, "CDsDomainTreeBrowser::FreeDomains");
if ( !ppDomainTree )
ExitGracefully(hr, E_INVALIDARG, "No pDomainTree");
if ( *ppDomainTree )
{
CoTaskMemFree(*ppDomainTree);
*ppDomainTree = NULL;
}
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr);
}
//---------------------------------------------------------------------------//
STDMETHODIMP CDsDomainTreeBrowser::FlushCachedDomains()
{
HRESULT hr;
TraceEnter(TRACE_DOMAIN, "CDsDomainTreeBrowser::FlushCachedDomains");
hr = FreeDomains(&g_pDomainTree);
FailGracefully(hr, "Failed to free cached domain list");
hr = FreeDomains(&_pDomainTree);
FailGracefully(hr, "Failed to free cached domain list (for search root)");
hr = S_OK; // success
exit_gracefully:
TraceLeaveResult(hr);
}