2811 lines
78 KiB
C++
2811 lines
78 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1998 - 1999
|
|
//
|
|
// File: domain.cpp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
#include "preDNSsn.h"
|
|
#include <SnapBase.h>
|
|
|
|
#include "resource.h"
|
|
#include "dnsutil.h"
|
|
#include "DNSSnap.h"
|
|
#include "snapdata.h"
|
|
|
|
#include "server.h"
|
|
#include "domain.h"
|
|
#include "record.h"
|
|
#include "zone.h"
|
|
|
|
#include "delegwiz.h"
|
|
|
|
#ifdef DEBUG_ALLOCATOR
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
#endif
|
|
|
|
BOOL _match(LPCWSTR lpszNSName,
|
|
CDNS_A_RecordNode* pARecordNode)
|
|
{
|
|
TRACE(_T("NS %s A %s\n"), lpszNSName, pARecordNode->GetString(0));
|
|
return DnsNameCompare_W((LPWSTR)lpszNSName, (LPWSTR)pARecordNode->GetString(0));
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// CNewDomainDialog
|
|
|
|
class CNewDomainDialog : public CHelpDialog
|
|
{
|
|
// Construction
|
|
public:
|
|
CNewDomainDialog(CDNSDomainNode* pParentDomainNode,
|
|
CComponentDataObject* pComponentData);
|
|
|
|
enum { IDD = IDD_DOMAIN_ADDNEWDOMAIN };
|
|
|
|
// Implementation
|
|
protected:
|
|
virtual BOOL OnInitDialog();
|
|
virtual void OnOK();
|
|
afx_msg void OnEditChange();
|
|
afx_msg void OnIPv4CtrlChange();
|
|
afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo);
|
|
DECLARE_MESSAGE_MAP()
|
|
private:
|
|
CDNSDomainNode* m_pParentDomainNode;
|
|
CComponentDataObject* m_pComponentData;
|
|
CEdit* GetDomainEdit() { return (CEdit*)GetDlgItem(IDC_EDIT_DOMAIN_NAME);}
|
|
CDNSIPv4Control* GetDomainIPv4Ctrl()
|
|
{ return (CDNSIPv4Control*)GetDlgItem(IDC_IPEDIT_DOMAIN_NAME);}
|
|
|
|
CString m_szDomainName;
|
|
BOOL m_bAdvancedView;
|
|
int m_nOctects;
|
|
int m_nUTF8ParentLen;
|
|
};
|
|
|
|
BEGIN_MESSAGE_MAP(CNewDomainDialog, CHelpDialog)
|
|
ON_EN_CHANGE(IDC_EDIT_DOMAIN_NAME,OnEditChange)
|
|
ON_EN_CHANGE(IDC_IPEDIT_DOMAIN_NAME, OnIPv4CtrlChange)
|
|
END_MESSAGE_MAP()
|
|
|
|
CNewDomainDialog::CNewDomainDialog(CDNSDomainNode* pParentDomainNode,
|
|
CComponentDataObject* pComponentData)
|
|
: CHelpDialog(CNewDomainDialog::IDD, pComponentData)
|
|
{
|
|
ASSERT(pParentDomainNode != NULL);
|
|
ASSERT(pComponentData != NULL);
|
|
m_pParentDomainNode = pParentDomainNode;
|
|
m_pComponentData = pComponentData;
|
|
m_bAdvancedView = TRUE;
|
|
m_nOctects = -1; // invalid if advanced view
|
|
m_nUTF8ParentLen = UTF8StringLen(pParentDomainNode->GetFullName());
|
|
}
|
|
|
|
BOOL CNewDomainDialog::OnInitDialog()
|
|
{
|
|
CHelpDialog::OnInitDialog();
|
|
|
|
// move the edit box in place of the IP control
|
|
CDNSIPv4Control* pNameIPCtrl = GetDomainIPv4Ctrl();
|
|
CEdit* pNameEdit = GetDomainEdit();
|
|
pNameEdit->SetLimitText(MAX_DNS_NAME_LEN - m_nUTF8ParentLen - 1);
|
|
CRect editRect;
|
|
pNameEdit->GetWindowRect(editRect);
|
|
ScreenToClient(editRect);
|
|
CRect ipRect;
|
|
pNameIPCtrl->GetWindowRect(ipRect);
|
|
ScreenToClient(ipRect);
|
|
ipRect.bottom = editRect.top + ipRect.Height();
|
|
ipRect.right = editRect.left + ipRect.Width();
|
|
ipRect.top = editRect.top;
|
|
ipRect.left = editRect.left;
|
|
pNameIPCtrl->MoveWindow(ipRect,TRUE);
|
|
|
|
// determine if we need/can have advanced view
|
|
CDNSRootData* pRootData = (CDNSRootData*)m_pComponentData->GetRootData();
|
|
ASSERT(pRootData != NULL);
|
|
m_bAdvancedView = pRootData->IsAdvancedView();
|
|
|
|
// force advanced view if we are in a forward lookup zone
|
|
if (!(m_pParentDomainNode->GetZoneNode()->IsReverse()))
|
|
m_bAdvancedView = TRUE;
|
|
|
|
// determine if we can have a normal view representation
|
|
CString szDomainName = m_pParentDomainNode->GetFullName();
|
|
if (!m_bAdvancedView)
|
|
{
|
|
// to have normal view we have to have a valid arpa suffix
|
|
BOOL bArpa = RemoveInAddrArpaSuffix(szDomainName.GetBuffer(1));
|
|
szDomainName.ReleaseBuffer(); // got "77.80.55.157"
|
|
if (!bArpa)
|
|
{
|
|
m_bAdvancedView = TRUE; // no need to toggle
|
|
}
|
|
else
|
|
{
|
|
m_nOctects = ReverseIPString(szDomainName.GetBuffer(1));
|
|
szDomainName.ReleaseBuffer(); // finally got "157.55.80.77"
|
|
// to have a normal view representation we cannot
|
|
// have more than 2 octects
|
|
if (m_nOctects > 2)
|
|
{
|
|
m_bAdvancedView = TRUE; // force advanced for classless
|
|
}
|
|
else
|
|
{
|
|
ASSERT(m_nOctects > 0);
|
|
switch(m_nOctects)
|
|
{
|
|
case 1: // e.g. "157", now "157._"
|
|
szDomainName += _T(".0.0"); // got "157._.0.0"
|
|
break;
|
|
case 2: // e.g. "157.55"
|
|
szDomainName += _T(".0"); // got "157.55._.0"
|
|
break;
|
|
};
|
|
// set the IP control with IP mask value
|
|
IP_ADDRESS ipAddr = IPStringToAddr(szDomainName);
|
|
ASSERT(ipAddr != INADDR_NONE);
|
|
pNameIPCtrl->SetIPv4Val(ipAddr);
|
|
|
|
for (int k=0; k<4; k++)
|
|
pNameIPCtrl->EnableField(k, k == m_nOctects);
|
|
}
|
|
}
|
|
|
|
} // if (!m_bAdvancedView)
|
|
|
|
|
|
// toggle text in static control
|
|
CDNSToggleTextControlHelper staticTextToggle;
|
|
UINT pnButtonStringIDs[2] = { IDS_NEW_DOMAIN_INST1, IDS_NEW_DOMAIN_INST2 };
|
|
VERIFY(staticTextToggle.Init(this, IDC_STATIC_TEXT, pnButtonStringIDs));
|
|
staticTextToggle.SetToggleState(m_bAdvancedView);
|
|
|
|
//
|
|
// enable/hide appropriate controls
|
|
//
|
|
if (m_bAdvancedView)
|
|
{
|
|
pNameIPCtrl->EnableWindow(FALSE);
|
|
pNameIPCtrl->ShowWindow(FALSE);
|
|
}
|
|
else
|
|
{
|
|
pNameEdit->EnableWindow(FALSE);
|
|
pNameEdit->ShowWindow(FALSE);
|
|
}
|
|
|
|
GetDlgItem(IDOK)->EnableWindow(!m_bAdvancedView);
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
|
}
|
|
|
|
void CNewDomainDialog::OnEditChange()
|
|
{
|
|
ASSERT(m_bAdvancedView);
|
|
|
|
//
|
|
// Get new name from control
|
|
//
|
|
GetDomainEdit()->GetWindowText(m_szDomainName);
|
|
|
|
//
|
|
// Trim white space
|
|
//
|
|
m_szDomainName.TrimLeft();
|
|
m_szDomainName.TrimRight();
|
|
|
|
//
|
|
// Enable OK button if its a valid name
|
|
//
|
|
CString szFullDomainName;
|
|
|
|
if (_wcsicmp(m_pParentDomainNode->GetFullName(), L".") == 0)
|
|
{
|
|
//
|
|
// If the parent domain is the root zone just check the name followed by a '.'
|
|
//
|
|
szFullDomainName.Format(L"%s.", m_szDomainName);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Else append the parent domain name to the new name
|
|
//
|
|
szFullDomainName.Format(L"%s.%s", m_szDomainName, m_pParentDomainNode->GetFullName());
|
|
}
|
|
|
|
//
|
|
// Get server flags
|
|
//
|
|
DWORD dwNameChecking = m_pParentDomainNode->GetServerNode()->GetNameCheckFlag();
|
|
|
|
//
|
|
// Is valid?
|
|
//
|
|
BOOL bIsValidName = (0 == ValidateDnsNameAgainstServerFlags(szFullDomainName,
|
|
DnsNameDomain,
|
|
dwNameChecking));
|
|
GetDlgItem(IDOK)->EnableWindow(bIsValidName);
|
|
}
|
|
|
|
void CNewDomainDialog::OnIPv4CtrlChange()
|
|
{
|
|
ASSERT(!m_bAdvancedView);
|
|
CDNSIPv4Control* pNameIPCtrl = GetDomainIPv4Ctrl();
|
|
DWORD dwArr[4];
|
|
pNameIPCtrl->GetArray(dwArr, 4);
|
|
BOOL bEmpty = (dwArr[m_nOctects] == FIELD_EMPTY);
|
|
if (!bEmpty)
|
|
{
|
|
ASSERT(dwArr[m_nOctects] <= 255);
|
|
m_szDomainName.Format(_T("%d"), dwArr[m_nOctects]);
|
|
}
|
|
GetDlgItem(IDOK)->EnableWindow(!bEmpty);
|
|
}
|
|
|
|
|
|
void CNewDomainDialog::OnOK()
|
|
{
|
|
RECORD_SEARCH recordSearch = RECORD_NOT_FOUND;
|
|
CDNSDomainNode* pNewParentDomain = NULL;
|
|
CString szFullRecordName = m_szDomainName + L"." + m_pParentDomainNode->GetFullName();
|
|
CString szNonExistentDomain;
|
|
|
|
recordSearch = m_pParentDomainNode->GetZoneNode()->DoesContain(szFullRecordName,
|
|
m_pComponentData,
|
|
&pNewParentDomain,
|
|
szNonExistentDomain,
|
|
TRUE);
|
|
|
|
if (recordSearch == RECORD_NOT_FOUND &&
|
|
pNewParentDomain != NULL)
|
|
{
|
|
DNS_STATUS err = pNewParentDomain->CreateSubdomain(m_szDomainName,m_pComponentData);
|
|
if (err != 0)
|
|
{
|
|
// creation error, warn the user and prompt again
|
|
DNSErrorDialog(err, IDS_MSG_DOMAIN_FAIL_CREATE);
|
|
CEdit* pDomainNameEdit = GetDomainEdit();
|
|
pDomainNameEdit->SetSel(0,-1);
|
|
pDomainNameEdit->SetFocus();
|
|
return;
|
|
}
|
|
}
|
|
else if (recordSearch == NON_EXISTENT_SUBDOMAIN && pNewParentDomain != NULL)
|
|
{
|
|
CDNSRootData* pRootData = (CDNSRootData*)m_pComponentData->GetRootData();
|
|
|
|
//
|
|
// Create the node on the server
|
|
//
|
|
CDNSDomainNode* pNewDomainNode = pNewParentDomain->CreateSubdomainNode();
|
|
if (pNewDomainNode == NULL)
|
|
{
|
|
ASSERT(pNewDomainNode != NULL);
|
|
return;
|
|
}
|
|
|
|
pNewParentDomain->SetSubdomainName(pNewDomainNode, m_szDomainName, pRootData->IsAdvancedView());
|
|
|
|
//
|
|
// tell the newly created object to write to the server
|
|
//
|
|
DNS_STATUS err = pNewDomainNode->Create();
|
|
if (err != 0)
|
|
{
|
|
DNSErrorDialog(err, IDS_MSG_DOMAIN_FAIL_CREATE);
|
|
return;
|
|
}
|
|
|
|
|
|
if (!szNonExistentDomain.IsEmpty())
|
|
{
|
|
//
|
|
// Create the first subdomain because the current domain is already enumerated
|
|
// so we have to start the remaining enumeration at the new subdomain that is needed
|
|
//
|
|
CDNSDomainNode* pSubdomainNode = pNewParentDomain->CreateSubdomainNode();
|
|
ASSERT(pSubdomainNode != NULL);
|
|
pNewParentDomain->SetSubdomainName(pSubdomainNode, szNonExistentDomain, pRootData->IsAdvancedView());
|
|
|
|
VERIFY(pNewParentDomain->AddChildToListAndUISorted(pSubdomainNode, m_pComponentData));
|
|
m_pComponentData->SetDescriptionBarText(pNewParentDomain);
|
|
|
|
//
|
|
// I don't care what the results of this are, I am just using it
|
|
// to do the expansion to the new record
|
|
//
|
|
recordSearch = pSubdomainNode->GetZoneNode()->DoesContain(szFullRecordName,
|
|
m_pComponentData,
|
|
&pNewParentDomain,
|
|
szNonExistentDomain,
|
|
TRUE);
|
|
}
|
|
}
|
|
else if (recordSearch == RECORD_NOT_FOUND_AT_THE_NODE)
|
|
{
|
|
//
|
|
// Do nothing since this is a domain and it already exists
|
|
//
|
|
}
|
|
else
|
|
{
|
|
DNS_STATUS err = m_pParentDomainNode->CreateSubdomain(m_szDomainName,m_pComponentData);
|
|
if (err != 0)
|
|
{
|
|
// creation error, warn the user and prompt again
|
|
DNSErrorDialog(err, IDS_MSG_DOMAIN_FAIL_CREATE);
|
|
CEdit* pDomainNameEdit = GetDomainEdit();
|
|
pDomainNameEdit->SetSel(0,-1);
|
|
pDomainNameEdit->SetFocus();
|
|
return;
|
|
}
|
|
}
|
|
CHelpDialog::OnOK();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// CDNSDomainQueryObj
|
|
|
|
class CDNSDomainMsg : public CObjBase
|
|
{
|
|
};
|
|
|
|
|
|
BOOL CDNSDomainQueryObj::Enumerate()
|
|
{
|
|
USES_CONVERSION;
|
|
TRACE(_T("CDNSDomainQueryObj::Enumerate(): Server <%s> Zone/Domain %s Enumerating\n"), (LPCTSTR)m_szServerName, (LPCTSTR)m_szNodeName);
|
|
|
|
DNS_STATUS err = 0;
|
|
|
|
// if needed, get the zone info
|
|
if (m_bIsZone && !m_bCache)
|
|
{
|
|
CDNSZoneInfoEx* pZoneInfo = new CDNSZoneInfoEx;
|
|
err = pZoneInfo->Query(m_szServerName, m_szFullNodeName, m_dwServerVersion);
|
|
if (err != 0)
|
|
{
|
|
delete pZoneInfo;
|
|
pZoneInfo = NULL;
|
|
OnError(err);
|
|
return FALSE; // no need to enumerate if we have no zone info
|
|
}
|
|
else
|
|
{
|
|
VERIFY(AddQueryResult(pZoneInfo));
|
|
}
|
|
}
|
|
|
|
|
|
// if executing a query for a specific RR type, just do it right away
|
|
if (m_wRecordType != DNS_TYPE_ALL)
|
|
{
|
|
// we assume that a type specific query does not have filtering enabled
|
|
ASSERT(m_bGetAll);
|
|
ASSERT(m_nFilterOption == DNS_QUERY_FILTER_DISABLED);
|
|
err = EnumerateFiltered(m_wRecordType);
|
|
if (err != 0)
|
|
OnError(err);
|
|
return FALSE; // we are done
|
|
}
|
|
|
|
// DO A MULTI PASS QUERY
|
|
m_bFirstPass = TRUE;
|
|
|
|
// there are items that cannot be filtered out for consistency
|
|
// (zone info, SOA, Ns, etc.), so we disable any filtering while
|
|
// getting them
|
|
BOOL bGetAllOld = m_bGetAll;
|
|
BOOL nFilterOptionOld = m_nFilterOption;
|
|
m_bGetAll = TRUE;
|
|
m_nFilterOption = DNS_QUERY_FILTER_DISABLED;
|
|
|
|
|
|
// only zones or the cache have SOA RR's
|
|
if (m_bIsZone || m_bCache)
|
|
{
|
|
err = EnumerateFiltered(DNS_TYPE_SOA);
|
|
if (err != 0)
|
|
{
|
|
OnError(err);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// only zones have WINS and NBSTAT RR's
|
|
if (m_bIsZone)
|
|
{
|
|
if (m_bReverse)
|
|
err = EnumerateFiltered(DNS_TYPE_NBSTAT);
|
|
else
|
|
err = EnumerateFiltered(DNS_TYPE_WINS);
|
|
if (err != 0)
|
|
{
|
|
OnError(err);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// need also to check for NS (zone or delegation)
|
|
err = EnumerateFiltered(DNS_TYPE_NS);
|
|
if (err != 0)
|
|
{
|
|
OnError(err);
|
|
return FALSE;
|
|
}
|
|
|
|
// add a message in the queue to signal we are done with
|
|
// the first phase
|
|
AddQueryResult(new CDNSDomainMsg);
|
|
|
|
// now query again, for all RR's, but need to filter the
|
|
// known types out
|
|
m_bFirstPass = FALSE;
|
|
|
|
// restore the filtering parameters we had before
|
|
m_bGetAll = bGetAllOld;
|
|
m_nFilterOption = nFilterOptionOld;
|
|
|
|
err = EnumerateFiltered(DNS_TYPE_ALL);
|
|
if (err != 0)
|
|
OnError(err);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DNS_STATUS CDNSDomainQueryObj::EnumerateFiltered(WORD wRecordType)
|
|
{
|
|
|
|
DWORD dwSelectFlags = m_dwSelectFlags;
|
|
// for single type queries, we do not want subfolders
|
|
if (wRecordType != DNS_TYPE_ALL)
|
|
dwSelectFlags |= DNS_RPC_VIEW_NO_CHILDREN;
|
|
|
|
return CDNSDomainNode::EnumerateNodes(m_szServerName,
|
|
m_szZoneName.IsEmpty() ? NULL : (LPCWSTR)m_szZoneName,
|
|
m_szNodeName,
|
|
m_szFullNodeName,
|
|
wRecordType,
|
|
dwSelectFlags,
|
|
m_bIsZone,
|
|
m_bReverse,
|
|
m_bAdvancedView,
|
|
this);
|
|
}
|
|
|
|
|
|
BOOL CDNSDomainQueryObj::CanAddRecord(WORD wRecordType, LPCWSTR lpszRecordName)
|
|
{
|
|
if (m_nFilterOption == DNS_QUERY_FILTER_DISABLED)
|
|
return TRUE; // we have no filtering at all
|
|
|
|
// determine if this is a special record type for filtered queries
|
|
BOOL bSpecialType = (wRecordType == DNS_TYPE_SOA) || (wRecordType == DNS_TYPE_NS) ||
|
|
(wRecordType == DNS_TYPE_WINS) || (wRecordType == DNS_TYPE_NBSTAT);
|
|
|
|
// in the first pass only special types allowed
|
|
if (m_bFirstPass)
|
|
return bSpecialType;
|
|
|
|
// in the second pass do not allow special types
|
|
if (!m_bFirstPass && bSpecialType)
|
|
return FALSE;
|
|
|
|
// we are left with normal types, apply the filtering, if required
|
|
if (m_nFilterOption == DNS_QUERY_FILTER_NONE)
|
|
return TRUE; // allow all
|
|
|
|
// need to match the record name
|
|
return MatchName(lpszRecordName);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// CDNSDomainNode
|
|
|
|
BEGIN_TOOLBAR_MAP(CDNSDomainNode)
|
|
TOOLBAR_EVENT(toolbarNewRecord, OnNewRecord)
|
|
END_TOOLBAR_MAP()
|
|
|
|
|
|
// {720132BA-44B2-11d1-B92F-00A0C9A06D2D}
|
|
const GUID CDNSDomainNode::NodeTypeGUID =
|
|
{ 0x720132ba, 0x44b2, 0x11d1, { 0xb9, 0x2f, 0x0, 0xa0, 0xc9, 0xa0, 0x6d, 0x2d } };
|
|
|
|
|
|
CDNSDomainNode::CDNSDomainNode(BOOL bDelegation)
|
|
{
|
|
m_bDelegation = bDelegation;
|
|
m_pZoneNode = NULL;
|
|
m_pNSRecordNodeList = new CDNS_NS_RecordNodeList;
|
|
ASSERT(m_pNSRecordNodeList != NULL);
|
|
m_bHasDataForPropPages = FALSE;
|
|
}
|
|
|
|
CDNSDomainNode::~CDNSDomainNode()
|
|
{
|
|
TRACE(_T("~CDNSDomainNode(), name <%s>\n"),GetDisplayName());
|
|
ASSERT(m_pNSRecordNodeList != NULL);
|
|
delete m_pNSRecordNodeList;
|
|
m_pNSRecordNodeList = NULL;
|
|
}
|
|
|
|
DWORD CDNSDomainNode::GetDefaultTTL()
|
|
{
|
|
if ( (m_pZoneNode != NULL) && (m_pZoneNode->GetZoneType() != DNS_ZONE_TYPE_CACHE) )
|
|
return m_pZoneNode->GetSOARecordMinTTL();
|
|
else
|
|
return (DWORD)0; // no info available from SOA RR
|
|
}
|
|
|
|
|
|
void CDNSDomainNode::SetFullDNSName(BOOL bIsZone,
|
|
BOOL,
|
|
LPCTSTR lpszNodeName,
|
|
LPCTSTR lpszParentFullName)
|
|
{
|
|
ASSERT(lpszNodeName != NULL);
|
|
ASSERT(lpszParentFullName != NULL);
|
|
|
|
if (bIsZone)
|
|
{
|
|
//
|
|
// the two names have to be the same, zone parent of itself
|
|
//
|
|
ASSERT(_wcsicmp(lpszParentFullName, lpszNodeName) == 0);
|
|
m_szFullName = lpszParentFullName;
|
|
}
|
|
else // it is a domain
|
|
{
|
|
ASSERT(_wcsicmp(lpszParentFullName, lpszNodeName) != 0);
|
|
|
|
//
|
|
// chain the node name to the parent full name to get the node's full name
|
|
//
|
|
if (lpszParentFullName[0] == L'.' )
|
|
{
|
|
//
|
|
// if parent is "." and name is "bar", get "bar.": this is the case for the root
|
|
// if parent is ".com" and name is "bar", get "bar.com":
|
|
//
|
|
m_szFullName.Format(_T("%s%s"), lpszNodeName,lpszParentFullName);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// if parent is "foo.com" and name is "bar", get "bar.foo.com"
|
|
//
|
|
m_szFullName.Format(_T("%s.%s"), lpszNodeName,lpszParentFullName);
|
|
}
|
|
}
|
|
TRACE(_T("CDNSDomainNode::SetFullDNSName() fullName = <%s>\n"), (LPCTSTR)m_szFullName);
|
|
}
|
|
|
|
void CDNSDomainNode::SetDisplayDNSName(BOOL bIsZone,
|
|
BOOL bReverse,
|
|
BOOL bAdvancedView,
|
|
LPCTSTR lpszNodeName,
|
|
LPCTSTR lpszParentFullName)
|
|
{
|
|
ASSERT(lpszNodeName != NULL);
|
|
ASSERT(lpszParentFullName != NULL);
|
|
|
|
if (_wcsicmp(lpszNodeName, L".") == 0)
|
|
{
|
|
CString szRootString;
|
|
VERIFY(szRootString.LoadString(IDS_ROOT_ZONE_LABEL));
|
|
m_szDisplayName = L"." + szRootString;
|
|
}
|
|
else
|
|
{
|
|
m_szDisplayName = lpszNodeName;
|
|
}
|
|
|
|
if (bIsZone && bReverse && !bAdvancedView)
|
|
{
|
|
CDNSZoneNode::SetZoneNormalViewHelper(m_szDisplayName);
|
|
}
|
|
}
|
|
|
|
|
|
void CDNSDomainNode::SetNames(BOOL bIsZone, BOOL bReverse, BOOL bAdvancedView,
|
|
LPCTSTR lpszNodeName, LPCTSTR lpszParentFullName)
|
|
{
|
|
ASSERT(lpszNodeName != NULL);
|
|
ASSERT(lpszParentFullName != NULL);
|
|
TRACE(_T("CDNSDomainNode::SetNames(bIsZone=%d, bReverse=%d, bAdvancedView=%d, lpszNodeName=<%s>, lpszParentFullName=<%s>)\n"),
|
|
bIsZone, bReverse, bAdvancedView, lpszNodeName,lpszParentFullName);
|
|
SetFullDNSName(bIsZone, bReverse, lpszNodeName, lpszParentFullName);
|
|
SetDisplayDNSName(bIsZone, bReverse, bAdvancedView, lpszNodeName, lpszParentFullName);
|
|
|
|
}
|
|
|
|
|
|
void CDNSDomainNode::ChangePTRRecordsViewOption(BOOL bAdvanced,
|
|
CComponentDataObject* pComponentDataObject)
|
|
{
|
|
POSITION pos;
|
|
for( pos = m_containerChildList.GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos);
|
|
|
|
// recurse down the tree
|
|
CDNSDomainNode* pDomainNode = dynamic_cast<CDNSDomainNode*>(pCurrentChild);
|
|
ASSERT(pDomainNode != NULL);
|
|
pDomainNode->ChangePTRRecordsViewOption(bAdvanced, pComponentDataObject);
|
|
}
|
|
|
|
POSITION leafPos;
|
|
for ( leafPos = m_leafChildList.GetHeadPosition(); leafPos != NULL; )
|
|
{
|
|
CTreeNode* pCurrentLeafNode = m_leafChildList.GetNext(leafPos);
|
|
CDNSRecordNodeBase* pRecordNode = dynamic_cast<CDNSRecordNodeBase*>(pCurrentLeafNode);
|
|
ASSERT(pRecordNode != NULL);
|
|
if (DNS_TYPE_PTR == pRecordNode->GetType())
|
|
{
|
|
CDNS_PTR_RecordNode* pPTRRecordNode = (CDNS_PTR_RecordNode*)pRecordNode;
|
|
pPTRRecordNode->ChangeDisplayName(this, bAdvanced);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
CQueryObj* CDNSDomainNode::OnCreateQuery()
|
|
{
|
|
// generic default setting
|
|
WORD wRecordType = DNS_TYPE_ALL;
|
|
DWORD dwSelectFlags = (m_pZoneNode->GetZoneType() == DNS_ZONE_TYPE_CACHE) ?
|
|
DNS_RPC_VIEW_CACHE_DATA : DNS_RPC_VIEW_AUTHORITY_DATA;
|
|
|
|
|
|
if (IsDelegation())
|
|
{
|
|
// special case the delegation: show only NS records and
|
|
// will have no children shown (delegation cut)
|
|
wRecordType = DNS_TYPE_NS;
|
|
dwSelectFlags = DNS_RPC_VIEW_GLUE_DATA | DNS_RPC_VIEW_NO_CHILDREN;
|
|
//dwSelectFlags = DNS_RPC_VIEW_GLUE_DATA |
|
|
// DNS_RPC_VIEW_NO_CHILDREN | DNS_RPC_VIEW_ADDITIONAL_DATA;
|
|
}
|
|
|
|
BOOL bCache = GetZoneNode()->GetZoneType() == DNS_ZONE_TYPE_CACHE;
|
|
LPCWSTR lpszZoneName = bCache ? NULL : m_pZoneNode->GetFullName();
|
|
|
|
|
|
CDNSRootData* pRootData = (CDNSRootData*)GetRootContainer();
|
|
ASSERT(pRootData != NULL);
|
|
CDNSDomainQueryObj* pQuery = new
|
|
CDNSDomainQueryObj(GetServerNode()->GetRPCName(),
|
|
lpszZoneName,
|
|
GetServerNode()->GetVersion(),
|
|
GetDisplayName(),
|
|
m_szFullName,
|
|
wRecordType,
|
|
dwSelectFlags,
|
|
IsZone(),
|
|
GetZoneNode()->IsReverse(),
|
|
bCache,
|
|
pRootData->IsAdvancedView());
|
|
|
|
// delegations will not have any filtering option (data consistency)
|
|
if (!IsDelegation())
|
|
{
|
|
pQuery->SetFilterOptions(pRootData->GetFilter());
|
|
}
|
|
return pQuery;
|
|
}
|
|
|
|
BOOL CDNSDomainNode::OnRefresh(CComponentDataObject* pComponentData,
|
|
CNodeList* pNodeList)
|
|
{
|
|
if (pNodeList->GetCount() > 1) // multiple selection
|
|
{
|
|
BOOL bRet = TRUE;
|
|
POSITION pos = pNodeList->GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
CTreeNode* pNode = pNodeList->GetNext(pos);
|
|
ASSERT(pNode != NULL);
|
|
|
|
CNodeList nodeList;
|
|
nodeList.AddTail(pNode);
|
|
if (!pNode->OnRefresh(pComponentData, &nodeList))
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
//
|
|
// single selection
|
|
//
|
|
if (CMTContainerNode::OnRefresh(pComponentData, pNodeList))
|
|
{
|
|
GetNSRecordNodeList()->RemoveAll();
|
|
m_bHasDataForPropPages = FALSE;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void CDNSDomainNode::OnThreadExitingNotification(CComponentDataObject* pComponentDataObject)
|
|
{
|
|
if (!m_bHasDataForPropPages)
|
|
{
|
|
// never got a CDNSDomainMsg notification object
|
|
// but we are done anyway, so change it back
|
|
m_bHasDataForPropPages = TRUE;
|
|
}
|
|
// call now the base class
|
|
CDNSMTContainerNode::OnThreadExitingNotification(pComponentDataObject);
|
|
}
|
|
|
|
void CDNSDomainNode::OnHaveData(CObjBase* pObj, CComponentDataObject* pComponentDataObject)
|
|
{
|
|
if (IS_CLASS(*pObj, CDNSDomainMsg))
|
|
{
|
|
// special case for a "message" object sent through to update verbs
|
|
TRACE(_T("Got CDNSDomainMsg\n"));
|
|
delete pObj;
|
|
ASSERT(!m_bHasDataForPropPages); // should get only once
|
|
m_bHasDataForPropPages = TRUE;
|
|
VERIFY(SUCCEEDED(pComponentDataObject->UpdateVerbState(this)));
|
|
return;
|
|
}
|
|
|
|
if (IS_CLASS(*pObj, CDNSDomainNode))
|
|
{
|
|
// assume all the child containers are derived from this class
|
|
CDNSDomainNode* pDomainNode = dynamic_cast<CDNSDomainNode*>(pObj);
|
|
pDomainNode->SetServerNode(GetServerNode());
|
|
pDomainNode->SetZone(m_pZoneNode);
|
|
}
|
|
else
|
|
{
|
|
OnHaveRecord(dynamic_cast<CDNSRecordNodeBase*>(pObj), pComponentDataObject);
|
|
}
|
|
AddChildToListAndUI(dynamic_cast<CTreeNode*>(pObj), pComponentDataObject);
|
|
pComponentDataObject->SetDescriptionBarText(this);
|
|
}
|
|
|
|
void CDNSDomainNode::OnHaveRecord(CDNSRecordNodeBase* pRecordNode,
|
|
CComponentDataObject* pComponentDataObject)
|
|
{
|
|
WORD wType = pRecordNode->GetType();
|
|
if (wType == DNS_TYPE_PTR)
|
|
{
|
|
ASSERT(pComponentDataObject != NULL); // assume this for PTR
|
|
CDNSRootData* pRootData = (CDNSRootData*)pComponentDataObject->GetRootData();
|
|
ASSERT(pRootData != NULL);
|
|
// if we are in normal view, have to change the
|
|
// default advanced representation
|
|
BOOL bAdvancedView = pRootData->IsAdvancedView();
|
|
if (!bAdvancedView)
|
|
((CDNS_PTR_RecordNode*)pRecordNode)->ChangeDisplayName(this, bAdvancedView);
|
|
}
|
|
else if (wType == DNS_TYPE_NS)
|
|
{
|
|
ASSERT(pRecordNode->IsAtTheNode());
|
|
GetNSRecordNodeList()->AddTail((CDNS_NS_RecordNode*)pRecordNode);
|
|
}
|
|
}
|
|
|
|
BOOL CDNSDomainNode::OnAddMenuItem(LPCONTEXTMENUITEM2 pContextMenuItem2,
|
|
long*)
|
|
{
|
|
if (pContextMenuItem2->lCommandID == IDM_SNAPIN_ADVANCED_VIEW)
|
|
{
|
|
pContextMenuItem2->fFlags = ((CDNSRootData*)GetRootContainer())->IsAdvancedView() ? MF_CHECKED : 0;
|
|
return TRUE;
|
|
}
|
|
if (pContextMenuItem2->lCommandID == IDM_SNAPIN_FILTERING)
|
|
{
|
|
if (((CDNSRootData*)GetRootContainer())->IsFilteringEnabled())
|
|
{
|
|
pContextMenuItem2->fFlags = MF_CHECKED;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD dwType = m_pZoneNode->GetZoneType();
|
|
BOOL bIsAutocreated = m_pZoneNode->IsAutocreated();
|
|
BOOL bIsSecondaryOrCache = (dwType == DNS_ZONE_TYPE_SECONDARY) ||
|
|
(dwType == DNS_ZONE_TYPE_CACHE) ||
|
|
(dwType == DNS_ZONE_TYPE_STUB);
|
|
BOOL bIsDelegatedDomain = !IsZone() && IsDelegation();
|
|
|
|
|
|
if (bIsSecondaryOrCache || bIsAutocreated || bIsDelegatedDomain)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// different add operations depending on the FWD/REV type
|
|
if (!GetZoneNode()->IsReverse() &&
|
|
(pContextMenuItem2->lCommandID == IDM_DOMAIN_NEW_PTR))
|
|
{
|
|
// do not add a PTR to a FWD lookup zone
|
|
return FALSE;
|
|
}
|
|
if (GetZoneNode()->IsReverse() &&
|
|
((pContextMenuItem2->lCommandID == IDM_DOMAIN_NEW_HOST) ||
|
|
(pContextMenuItem2->lCommandID == IDM_DOMAIN_NEW_MX)))
|
|
{
|
|
// do not add a HOST, MX, ALIAS to a REV lookup zone
|
|
return FALSE;
|
|
}
|
|
|
|
// have the menu item added. but it might be grayed out...
|
|
if (m_nState != loaded)
|
|
{
|
|
pContextMenuItem2->fFlags |= MF_GRAYED;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT CDNSDomainNode::OnSetToolbarVerbState(IToolbar* pToolbar,
|
|
CNodeList* pNodeList)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Set the button state for each button on the toolbar
|
|
//
|
|
hr = pToolbar->SetButtonState(toolbarNewServer, ENABLED, FALSE);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
hr = pToolbar->SetButtonState(toolbarNewZone, ENABLED, FALSE);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
if (pNodeList->GetCount() > 1) // multiple selection
|
|
{
|
|
hr = pToolbar->SetButtonState(toolbarNewRecord, ENABLED, FALSE);
|
|
}
|
|
else if (pNodeList->GetCount() == 1) // single selection
|
|
{
|
|
DWORD dwType = m_pZoneNode->GetZoneType();
|
|
BOOL bIsAutocreated = m_pZoneNode->IsAutocreated();
|
|
BOOL bIsSecondaryOrCache = (dwType == DNS_ZONE_TYPE_SECONDARY) ||
|
|
(dwType == DNS_ZONE_TYPE_CACHE) ||
|
|
(dwType == DNS_ZONE_TYPE_STUB);
|
|
BOOL bIsDelegatedDomain = !IsZone() && IsDelegation();
|
|
|
|
BOOL bEnable = TRUE;
|
|
if (bIsSecondaryOrCache || bIsAutocreated || bIsDelegatedDomain)
|
|
{
|
|
bEnable = FALSE;
|
|
}
|
|
hr = pToolbar->SetButtonState(toolbarNewRecord, ENABLED, bEnable);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
BOOL CDNSDomainNode::OnSetDeleteVerbState(DATA_OBJECT_TYPES type,
|
|
BOOL* pbHide,
|
|
CNodeList* pNodeList)
|
|
{
|
|
if (pNodeList->GetCount() > 1) // multiple selection
|
|
{
|
|
BOOL bRet = TRUE;
|
|
BOOL bRetHide = FALSE;
|
|
*pbHide = FALSE;
|
|
|
|
POSITION pos = pNodeList->GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
CTreeNode* pNode = pNodeList->GetNext(pos);
|
|
ASSERT(pNode != NULL);
|
|
|
|
CNodeList nodeList;
|
|
nodeList.AddTail(pNode);
|
|
if (!pNode->OnSetDeleteVerbState(type, &bRetHide, &nodeList))
|
|
{
|
|
bRet = FALSE;
|
|
break;
|
|
}
|
|
if (bRetHide)
|
|
{
|
|
*pbHide = TRUE;
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
*pbHide = FALSE;
|
|
DWORD dwType = m_pZoneNode->GetZoneType();
|
|
BOOL bIsAutocreated = m_pZoneNode->IsAutocreated();
|
|
|
|
if (IsThreadLocked())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// cannot delete from an autocreate zone/domain
|
|
//
|
|
if (bIsAutocreated)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// cannot delete from a secondary or stub zone, but can delete the zone itself
|
|
// cannot delete the cache
|
|
//
|
|
if (
|
|
(
|
|
( ((dwType == DNS_ZONE_TYPE_SECONDARY) || (dwType == DNS_ZONE_TYPE_STUB)) && !IsZone() ) || // subdomain of secondary
|
|
( (dwType == DNS_ZONE_TYPE_CACHE) && IsZone() ) // cache zone itself
|
|
)
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CDNSDomainNode::OnSetRefreshVerbState(DATA_OBJECT_TYPES,
|
|
BOOL* pbHide,
|
|
CNodeList*)
|
|
{
|
|
*pbHide = FALSE;
|
|
return !IsThreadLocked();
|
|
}
|
|
|
|
|
|
HRESULT CDNSDomainNode::OnCommand(long nCommandID,
|
|
DATA_OBJECT_TYPES,
|
|
CComponentDataObject* pComponentData,
|
|
CNodeList* pNodeList)
|
|
{
|
|
if (pNodeList->GetCount() > 1) // multiple selection
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
switch (nCommandID)
|
|
{
|
|
case IDM_DOMAIN_NEW_DOMAIN:
|
|
OnNewDomain(pComponentData);
|
|
break;
|
|
case IDM_DOMAIN_NEW_DELEGATION:
|
|
OnNewDelegation(pComponentData);
|
|
break;
|
|
case IDM_DOMAIN_NEW_RECORD:
|
|
OnNewRecord(pComponentData, pNodeList);
|
|
break;
|
|
case IDM_DOMAIN_NEW_HOST:
|
|
OnNewHost(pComponentData);
|
|
break;
|
|
case IDM_DOMAIN_NEW_ALIAS:
|
|
OnNewAlias(pComponentData);
|
|
break;
|
|
case IDM_DOMAIN_NEW_MX:
|
|
OnNewMailExchanger(pComponentData);
|
|
break;
|
|
case IDM_DOMAIN_NEW_PTR:
|
|
OnNewPointer(pComponentData);
|
|
break;
|
|
case IDM_SNAPIN_ADVANCED_VIEW:
|
|
((CDNSRootData*)pComponentData->GetRootData())->OnViewOptions(pComponentData);
|
|
break;
|
|
case IDM_SNAPIN_FILTERING:
|
|
{
|
|
if(((CDNSRootData*)pComponentData->GetRootData())->OnFilteringOptions(pComponentData))
|
|
{
|
|
pComponentData->SetDescriptionBarText(this);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
ASSERT(FALSE); // Unknown command!
|
|
return E_FAIL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
LPWSTR CDNSDomainNode::GetDescriptionBarText()
|
|
{
|
|
static CString szFilterEnabled;
|
|
static CString szRecordsFormat;
|
|
|
|
INT_PTR nContainerCount = GetContainerChildList()->GetCount();
|
|
INT_PTR nLeafCount = GetLeafChildList()->GetCount();
|
|
|
|
//
|
|
// If not already loaded, then load the format string L"%d record(s)"
|
|
//
|
|
if (szRecordsFormat.IsEmpty())
|
|
{
|
|
szRecordsFormat.LoadString(IDS_FORMAT_RECORDS);
|
|
}
|
|
|
|
//
|
|
// Format the child count into the description bar text
|
|
//
|
|
m_szDescriptionBar.Format(szRecordsFormat, nContainerCount + nLeafCount);
|
|
|
|
//
|
|
// Add L"[Filter Activated]" if the filter is on
|
|
//
|
|
if(((CDNSRootData*)GetRootContainer())->IsFilteringEnabled())
|
|
{
|
|
//
|
|
// If not already loaded, then load the L"[Filter Activated]" string
|
|
//
|
|
if (szFilterEnabled.IsEmpty())
|
|
{
|
|
szFilterEnabled.LoadString(IDS_FILTER_ENABLED);
|
|
}
|
|
m_szDescriptionBar += szFilterEnabled;
|
|
}
|
|
|
|
return (LPWSTR)(LPCWSTR)m_szDescriptionBar;
|
|
}
|
|
|
|
int CDNSDomainNode::GetImageIndex(BOOL)
|
|
{
|
|
int nIndex = 0;
|
|
BOOL bDelegation = IsDelegation();
|
|
switch (m_nState)
|
|
{
|
|
case notLoaded:
|
|
nIndex = bDelegation ? DELEGATED_DOMAIN_IMAGE_NOT_LOADED : DOMAIN_IMAGE_NOT_LOADED;
|
|
break;
|
|
case loading:
|
|
nIndex = bDelegation ? DELEGATED_DOMAIN_IMAGE_LOADING : DOMAIN_IMAGE_LOADING;
|
|
break;
|
|
case loaded:
|
|
nIndex = bDelegation ? DELEGATED_DOMAIN_IMAGE_LOADED : DOMAIN_IMAGE_LOADED;
|
|
break;
|
|
case unableToLoad:
|
|
nIndex = bDelegation ? DELEGATED_DOMAIN_IMAGE_UNABLE_TO_LOAD : DOMAIN_IMAGE_UNABLE_TO_LOAD;
|
|
break;
|
|
case accessDenied:
|
|
nIndex = bDelegation ? DELEGATED_DOMAIN_IMAGE_ACCESS_DENIED : DOMAIN_IMAGE_ACCESS_DENIED;
|
|
break;
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
|
|
void CDNSDomainNode::OnDelete(CComponentDataObject* pComponentData,
|
|
CNodeList* pNodeList)
|
|
{
|
|
if (pNodeList->GetCount() > 1) // multiple selection
|
|
{
|
|
OnMultiselectDelete(pComponentData, pNodeList);
|
|
return;
|
|
}
|
|
|
|
UINT nRet = DNSConfirmOperation(IDS_MSG_DOMAIN_DELETE, this);
|
|
if (IDNO == nRet ||
|
|
IDCANCEL == nRet)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (IsSheetLocked())
|
|
{
|
|
if (!CanCloseSheets())
|
|
return;
|
|
pComponentData->GetPropertyPageHolderTable()->DeleteSheetsOfNode(this);
|
|
}
|
|
ASSERT(!IsSheetLocked());
|
|
|
|
DNS_STATUS err = Delete();
|
|
if (err != 0)
|
|
{
|
|
DNSErrorDialog(err, IDS_MSG_DOMAIN_FAIL_DELETE);
|
|
return;
|
|
}
|
|
// now remove from the UI and from the cache
|
|
DeleteHelper(pComponentData);
|
|
delete this; // gone
|
|
}
|
|
|
|
void CDNSDomainNode::OnMultiselectDelete(CComponentDataObject* pComponentData,
|
|
CNodeList* pNodeList)
|
|
{
|
|
UINT nRet = DNSConfirmOperation(IDS_MSG_DOMAIN_MULTI_DELETE, this);
|
|
if (IDCANCEL == nRet ||
|
|
IDNO == nRet)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DNS_STATUS* errArray = new DNS_STATUS[pNodeList->GetCount()];
|
|
if (errArray == NULL)
|
|
{
|
|
DNSErrorDialog(E_OUTOFMEMORY, IDS_MSG_DOMAIN_FAIL_DELETE);
|
|
return;
|
|
}
|
|
|
|
memset(errArray, 0, sizeof(DNS_STATUS) * pNodeList->GetCount());
|
|
|
|
BOOL bErrorOccurred = FALSE;
|
|
UINT idx = 0;
|
|
POSITION pos = pNodeList->GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
CTreeNode* pTreeNode = pNodeList->GetNext(pos);
|
|
if (pTreeNode != NULL)
|
|
{
|
|
if (pTreeNode->IsSheetLocked())
|
|
{
|
|
if (!pTreeNode->CanCloseSheets())
|
|
{
|
|
idx++;
|
|
continue;
|
|
}
|
|
pComponentData->GetPropertyPageHolderTable()->DeleteSheetsOfNode(pTreeNode);
|
|
}
|
|
ASSERT(!pTreeNode->IsSheetLocked());
|
|
|
|
CDNSDomainNode* pDomainNode = dynamic_cast<CDNSDomainNode*>(pTreeNode);
|
|
if (pDomainNode != NULL)
|
|
{
|
|
errArray[idx] = pDomainNode->Delete();
|
|
if (errArray[idx] != 0)
|
|
{
|
|
bErrorOccurred = TRUE;
|
|
idx++;
|
|
continue;
|
|
}
|
|
//
|
|
// now remove from the UI and from the cache
|
|
//
|
|
pDomainNode->DeleteHelper(pComponentData);
|
|
delete pDomainNode; // gone
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If its not a domain node then it must be a record node
|
|
//
|
|
CDNSRecordNodeBase* pRecordNode = dynamic_cast<CDNSRecordNodeBase*>(pTreeNode);
|
|
if (pRecordNode != NULL)
|
|
{
|
|
errArray[idx] = pRecordNode->DeleteOnServerAndUI(pComponentData);
|
|
if (errArray[idx] != 0)
|
|
{
|
|
bErrorOccurred = TRUE;
|
|
idx++;
|
|
continue;
|
|
}
|
|
delete pRecordNode; // gone
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// What type of node is this???
|
|
//
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
}
|
|
idx++;
|
|
}
|
|
|
|
//
|
|
// Now display the errors in some meaningful manner
|
|
//
|
|
if (bErrorOccurred)
|
|
{
|
|
CMultiselectErrorDialog dlg;
|
|
|
|
CString szTitle;
|
|
CString szCaption;
|
|
CString szColumnHeader;
|
|
|
|
VERIFY(szTitle.LoadString(IDS_MULTISELECT_ERROR_DIALOG_TITLE));
|
|
VERIFY(szCaption.LoadString(IDS_MULTISELECT_ERROR_DIALOG_CAPTION));
|
|
VERIFY(szColumnHeader.LoadString(IDS_MULTISELECT_ERROR_DIALOG_COLUMN_HEADER));
|
|
|
|
HRESULT hr = dlg.Initialize(pNodeList,
|
|
errArray,
|
|
static_cast<UINT>(pNodeList->GetCount()),
|
|
szTitle,
|
|
szCaption,
|
|
szColumnHeader);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
dlg.DoModal();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDNSDomainNode::OnNewDomain(CComponentDataObject* pComponentData)
|
|
{
|
|
CNewDomainDialog dlg(this, pComponentData);
|
|
// the dialog will do the creation
|
|
dlg.DoModal();
|
|
}
|
|
|
|
void CDNSDomainNode::OnNewDelegation(CComponentDataObject* pComponentData)
|
|
{
|
|
ASSERT(pComponentData != NULL);
|
|
CDNSMTContainerNode* pContainerNode = (CDNSMTContainerNode*)GetContainer();
|
|
ASSERT(pContainerNode != NULL);
|
|
CDNSDelegationWizardHolder* pHolder =
|
|
new CDNSDelegationWizardHolder(pContainerNode, this, pComponentData);
|
|
ASSERT(pHolder != NULL);
|
|
pHolder->DoModalWizard();
|
|
}
|
|
|
|
RECORD_SEARCH CDNSDomainNode::DoesContain(PCWSTR pszRecName,
|
|
CComponentDataObject* pComponentData,
|
|
CDNSDomainNode** ppDomainNode,
|
|
CString& szNonExistentDomain,
|
|
BOOL bExpandNodes)
|
|
{
|
|
#if TRUE
|
|
CDNSNameTokenizer recordTokenizer(pszRecName);
|
|
CDNSNameTokenizer domainTokenizer((IsZone()) ? GetFullName() : GetDisplayName());
|
|
|
|
if (!recordTokenizer.Tokenize(L".") || !domainTokenizer.Tokenize(L"."))
|
|
{
|
|
*ppDomainNode = NULL;
|
|
return RECORD_NOT_FOUND;
|
|
}
|
|
|
|
|
|
|
|
recordTokenizer.RemoveMatchingFromTail(domainTokenizer);
|
|
|
|
if (recordTokenizer.GetCount() == 0 && domainTokenizer.GetCount() == 0)
|
|
{
|
|
//
|
|
// Record is "At the node"
|
|
//
|
|
*ppDomainNode = this;
|
|
return RECORD_NOT_FOUND_AT_THE_NODE;
|
|
}
|
|
else if ((recordTokenizer.GetCount() == 0 && domainTokenizer.GetCount() != 0) ||
|
|
(recordTokenizer.GetCount() != 0 && domainTokenizer.GetCount() != 0))
|
|
{
|
|
//
|
|
// I don't understand how we got in this situation. It means we are searching in
|
|
// the wrong domain.
|
|
//
|
|
ASSERT(FALSE);
|
|
*ppDomainNode = NULL;
|
|
return RECORD_NOT_FOUND;
|
|
}
|
|
else // recordTokenizer.GetCount() != 0 && domainTokenizer.GetCount() == 0
|
|
{
|
|
//
|
|
// Need to search the children lists
|
|
//
|
|
|
|
//
|
|
// If the node hasn't been enumerated, do that now
|
|
//
|
|
if (!IsEnumerated())
|
|
{
|
|
if (!bExpandNodes)
|
|
{
|
|
*ppDomainNode = this;
|
|
return DOMAIN_NOT_ENUMERATED;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Expand the node
|
|
//
|
|
HWND hWnd = NULL;
|
|
HRESULT hr = pComponentData->GetConsole()->GetMainWindow(&hWnd);
|
|
ASSERT(SUCCEEDED(hr));
|
|
CWnd* pParentWnd = CWnd::FromHandle(hWnd);
|
|
CLongOperationDialog dlg(
|
|
new CNodeEnumerationThread(pComponentData, this),
|
|
pParentWnd,
|
|
IDR_SEARCH_AVI);
|
|
dlg.DoModal();
|
|
}
|
|
}
|
|
|
|
CString szRemaining;
|
|
recordTokenizer.GetRemaining(szRemaining, L".");
|
|
|
|
//
|
|
// Search for domains that match the last token remaining in the record name
|
|
//
|
|
POSITION pos = m_containerChildList.GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos);
|
|
|
|
CDNSDomainNode* pDomainNode = dynamic_cast<CDNSDomainNode*>(pCurrentChild);
|
|
if (pDomainNode == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
continue;
|
|
}
|
|
|
|
if (_wcsicmp(pDomainNode->GetDisplayName(), recordTokenizer.GetTail()) == 0)
|
|
{
|
|
//
|
|
// Found a sub-domain in the path that we have in the UI
|
|
// recurse to see if it or any of its child match pszFullName
|
|
//
|
|
return pDomainNode->DoesContain(szRemaining, pComponentData, ppDomainNode, szNonExistentDomain, bExpandNodes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the remaining name doesn't match a domain and there
|
|
// is still a '.' in it then there is a non-existent domain
|
|
//
|
|
if (szRemaining.Find(L'.') != -1)
|
|
{
|
|
szNonExistentDomain = recordTokenizer.GetTail();
|
|
*ppDomainNode = this;
|
|
return NON_EXISTENT_SUBDOMAIN;
|
|
}
|
|
|
|
//
|
|
// Since no domains match, lets check the records
|
|
//
|
|
pos = m_leafChildList.GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos);
|
|
if (pCurrentChild == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
continue;
|
|
}
|
|
|
|
if (_wcsicmp(pCurrentChild->GetDisplayName(), szRemaining) == 0)
|
|
{
|
|
//
|
|
// We found the record and its in this domain
|
|
//
|
|
*ppDomainNode = this;
|
|
return RECORD_FOUND;
|
|
}
|
|
}
|
|
}
|
|
|
|
*ppDomainNode = this;
|
|
return RECORD_NOT_FOUND;
|
|
|
|
#else
|
|
//
|
|
// The fast way that doesn't quite work
|
|
//
|
|
CString szDomainFullName = GetFullName();
|
|
CString szFullName = pszFullName;
|
|
|
|
//
|
|
// Check to see if the end of the names are equal
|
|
//
|
|
int iFindResult = szFullName.Find(szDomainFullName);
|
|
if (iFindResult == -1)
|
|
{
|
|
//
|
|
// If they are not we are never going to find them
|
|
//
|
|
*ppDomainNode = NULL;
|
|
return RECORD_NOT_FOUND;
|
|
}
|
|
|
|
if (iFindResult == 0)
|
|
{
|
|
//
|
|
// We found it
|
|
//
|
|
*ppDomainNode = this;
|
|
return RECORD_FOUND;
|
|
}
|
|
|
|
//
|
|
// Remove the matching parts plus the trailing dot
|
|
// This should leave us with something like foo.bar or just foo
|
|
//
|
|
CString szRelativeName = szFullName.Left(iFindResult - 1);
|
|
ASSERT(!szRelativeName.IsEmpty());
|
|
|
|
CString szChild;
|
|
CString szRemaining;
|
|
|
|
//
|
|
// Now search for a dot starting on the right
|
|
//
|
|
iFindResult = szRelativeName.ReverseFind(L'.');
|
|
|
|
if (iFindResult == -1)
|
|
{
|
|
//
|
|
// The relative name doesn't contain a dot so it is the child
|
|
// we are looking for
|
|
//
|
|
szChild = szRelativeName;
|
|
}
|
|
else
|
|
{
|
|
szChild = szRelativeName.Right(szRelativeName.GetLength() - iFindResult - 1);
|
|
szRemaining = szRelativeName;
|
|
}
|
|
|
|
//
|
|
// If the node hasn't been enumerated, do that now
|
|
//
|
|
if (!IsEnumerated())
|
|
{
|
|
if (!bExpandNodes)
|
|
{
|
|
*ppDomainNode = this;
|
|
return DOMAIN_NOT_ENUMERATED;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Expand the node
|
|
//
|
|
HWND hWnd = NULL;
|
|
HRESULT hr = pComponentData->GetConsole()->GetMainWindow(&hWnd);
|
|
ASSERT(SUCCEEDED(hr));
|
|
CWnd* pParentWnd = CWnd::FromHandle(hWnd);
|
|
CLongOperationDialog dlg(
|
|
new CNodeEnumerationThread(pComponentData, this),
|
|
pParentWnd,
|
|
IDR_SEARCH_AVI);
|
|
dlg.DoModal();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Search for domains that match this name
|
|
//
|
|
POSITION pos = m_containerChildList.GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos);
|
|
|
|
CDNSDomainNode* pDomainNode = dynamic_cast<CDNSDomainNode*>(pCurrentChild);
|
|
if (pDomainNode == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
continue;
|
|
}
|
|
|
|
if (_wcsicmp(pDomainNode->GetDisplayName(), szChild) == 0)
|
|
{
|
|
//
|
|
// Found a sub-domain in the path that we have in the UI
|
|
// recurse to see if it or any of its child match pszFullName
|
|
//
|
|
return pDomainNode->DoesContain(pszFullName, pComponentData, ppDomainNode, szNonExistentDomain, bExpandNodes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Since no domains match, lets check the records
|
|
//
|
|
pos = m_leafChildList.GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos);
|
|
if (pCurrentChild == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
continue;
|
|
}
|
|
|
|
if (_wcsicmp(pCurrentChild->GetDisplayName(), szChild) == 0)
|
|
{
|
|
//
|
|
// We found the record and its in this domain
|
|
//
|
|
*ppDomainNode = this;
|
|
return RECORD_FOUND;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Well, we didn't find the record, If the remaining part of the name
|
|
// still contains a '.' then we know the domain the that contains the
|
|
// record is not in the UI so return NULL. If there isn't a '.' then
|
|
// we are at the correct level but just couldn't find the record. Return
|
|
// this domain.
|
|
//
|
|
if (szRemaining.Find(L'.') == -1)
|
|
{
|
|
*ppDomainNode = this;
|
|
return RECORD_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We were not able to find a subdomain that the record should be in
|
|
//
|
|
*ppDomainNode = this;
|
|
return NON_EXISTENT_SUBDOMAIN;
|
|
}
|
|
return RECORD_NOT_FOUND;
|
|
#endif
|
|
}
|
|
|
|
|
|
CDNSDomainNode* CDNSDomainNode::FindSubdomainNode(LPCTSTR lpszSubdomainNode)
|
|
{
|
|
//
|
|
// assume the string is the name of the subnode as FQDN
|
|
//
|
|
|
|
//
|
|
// Check the current node first since it could be zone that is a delegation of
|
|
// one of the protocol domains
|
|
//
|
|
if (_wcsicmp(GetFullName(), lpszSubdomainNode) == 0)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
POSITION pos;
|
|
for( pos = m_containerChildList.GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CTreeNode* pCurrentChild = m_containerChildList.GetNext(pos);
|
|
|
|
CDNSDomainNode* pSubDomainNode = dynamic_cast<CDNSDomainNode*>(pCurrentChild);
|
|
ASSERT(pSubDomainNode != NULL);
|
|
if (_wcsicmp(pSubDomainNode->GetFullName(), lpszSubdomainNode) == 0)
|
|
{
|
|
return pSubDomainNode;
|
|
}
|
|
}
|
|
return NULL; // not found
|
|
}
|
|
|
|
CDNSDomainNode* CDNSDomainNode::CreateSubdomainNode(BOOL bDelegation)
|
|
{
|
|
CDNSDomainNode* pNode = new CDNSDomainNode(bDelegation);
|
|
pNode->SetServerNode(GetServerNode());
|
|
ASSERT(m_pZoneNode != NULL);
|
|
pNode->SetZone(m_pZoneNode);
|
|
return pNode;
|
|
}
|
|
|
|
void CDNSDomainNode::SetSubdomainName(CDNSDomainNode* pSubdomainNode,
|
|
LPCTSTR lpszSubdomainName, BOOL bAdvancedView)
|
|
{
|
|
ASSERT(m_pZoneNode != NULL);
|
|
ASSERT(pSubdomainNode != NULL);
|
|
BOOL bReverse = GetZoneNode()->IsReverse();
|
|
pSubdomainNode->SetNames(FALSE, bReverse, bAdvancedView, lpszSubdomainName, GetFullName());
|
|
}
|
|
|
|
|
|
DNS_STATUS CDNSDomainNode::CreateSubdomain(CDNSDomainNode* pSubdomainNode,
|
|
CComponentDataObject* pComponentData)
|
|
{
|
|
// tell the newly created object to write to the server
|
|
DNS_STATUS err = pSubdomainNode->Create();
|
|
if (err == 0)
|
|
{
|
|
// success, add to the UI
|
|
VERIFY(AddChildToListAndUI(pSubdomainNode, pComponentData));
|
|
pComponentData->SetDescriptionBarText(this);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
DNS_STATUS CDNSDomainNode::CreateSubdomain(LPCTSTR lpszDomainName,
|
|
CComponentDataObject* pComponentData)
|
|
{
|
|
CDNSDomainNode* pSubdomainNode = CreateSubdomainNode();
|
|
ASSERT(pSubdomainNode != NULL);
|
|
CDNSRootData* pRootData = (CDNSRootData*)pComponentData->GetRootData();
|
|
SetSubdomainName(pSubdomainNode, lpszDomainName, pRootData->IsAdvancedView());
|
|
|
|
// tell the newly created object to write to the server
|
|
DNS_STATUS err = CreateSubdomain(pSubdomainNode, pComponentData);
|
|
if (err != 0)
|
|
{
|
|
// something went wrong, bail out
|
|
delete pSubdomainNode;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
void CDNSDomainNode::OnNewRecordHelper(CComponentDataObject* pComponentData, WORD wType)
|
|
{
|
|
ASSERT(pComponentData != NULL);
|
|
if (wType == 0)
|
|
{
|
|
CSelectDNSRecordTypeDialog dlg(this, pComponentData);
|
|
dlg.DoModal();
|
|
}
|
|
else
|
|
{
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
CString szTitle;
|
|
szTitle.LoadString(IDS_NEW_RECORD_TITLE);
|
|
CDNSRecordPropertyPageHolder recordHolder(this, NULL, pComponentData, wType);
|
|
recordHolder.DoModalDialog(szTitle);
|
|
}
|
|
}
|
|
|
|
HRESULT CDNSDomainNode::OnNewRecord(CComponentDataObject* pComponentData,
|
|
CNodeList* pNodeList)
|
|
{
|
|
ASSERT(pNodeList->GetCount() == 1);
|
|
OnNewRecordHelper(pComponentData, 0);
|
|
return S_OK;
|
|
}
|
|
|
|
void CDNSDomainNode::OnNewHost(CComponentDataObject* pComponentData)
|
|
{
|
|
//AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
//OnNewRecordHelper(pComponentData, DNS_TYPE_A);
|
|
CNewHostDialog dlg(this, pComponentData);
|
|
dlg.DoModal();
|
|
}
|
|
|
|
void CDNSDomainNode::OnNewAlias(CComponentDataObject* pComponentData)
|
|
{
|
|
OnNewRecordHelper(pComponentData, DNS_TYPE_CNAME);
|
|
}
|
|
|
|
void CDNSDomainNode::OnNewMailExchanger(CComponentDataObject* pComponentData)
|
|
{
|
|
OnNewRecordHelper(pComponentData, DNS_TYPE_MX);
|
|
}
|
|
|
|
void CDNSDomainNode::OnNewPointer(CComponentDataObject* pComponentData)
|
|
{
|
|
OnNewRecordHelper(pComponentData, DNS_TYPE_PTR);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// display of property pages
|
|
|
|
BOOL CDNSDomainNode::HasPropertyPages(DATA_OBJECT_TYPES,
|
|
BOOL* pbHideVerb,
|
|
CNodeList* pNodeList)
|
|
{
|
|
if (pNodeList->GetCount() > 1) // multiple selection
|
|
{
|
|
*pbHideVerb = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
*pbHideVerb = FALSE; // always show the verb
|
|
|
|
if (!m_bHasDataForPropPages)
|
|
return FALSE;
|
|
|
|
// cannot have property pages only in loaded state
|
|
//if (m_nState != loaded)
|
|
// return FALSE;
|
|
// have pages if it is a delegation
|
|
return IsDelegation();
|
|
}
|
|
|
|
HRESULT CDNSDomainNode::CreatePropertyPages(LPPROPERTYSHEETCALLBACK lpProvider,
|
|
LONG_PTR handle,
|
|
CNodeList* pNodeList)
|
|
{
|
|
ASSERT(pNodeList->GetCount() == 1); // multi-select not supported
|
|
|
|
ASSERT(m_bHasDataForPropPages);
|
|
ASSERT(IsDelegation() || GetZoneNode()->IsDSIntegrated());
|
|
if (GetSheetCount() > 0)
|
|
{
|
|
CComponentDataObject* pComponentDataObject =
|
|
((CRootData*)(GetContainer()->GetRootContainer()))->GetComponentDataObject();
|
|
ASSERT(pComponentDataObject != NULL);
|
|
pComponentDataObject->GetPropertyPageHolderTable()->BroadcastSelectPage(this, DOMAIN_HOLDER_NS);
|
|
return S_OK;
|
|
}
|
|
return CreatePropertyPagesHelper(lpProvider, handle, DOMAIN_HOLDER_NS);
|
|
}
|
|
|
|
|
|
void CDNSDomainNode::Show(BOOL bShow, CComponentDataObject* pComponentData)
|
|
{
|
|
CDNSMTContainerNode::Show(bShow, pComponentData);
|
|
if (!bShow)
|
|
GetNSRecordNodeList()->RemoveAll();
|
|
}
|
|
|
|
HRESULT CDNSDomainNode::CreatePropertyPagesHelper(LPPROPERTYSHEETCALLBACK lpProvider,
|
|
LONG_PTR handle, long)
|
|
{
|
|
CComponentDataObject* pComponentDataObject =
|
|
((CRootData*)(GetContainer()->GetRootContainer()))->GetComponentDataObject();
|
|
ASSERT(pComponentDataObject != NULL);
|
|
|
|
CDNSDomainPropertyPageHolder* pHolder =
|
|
new CDNSDomainPropertyPageHolder((CDNSDomainNode*)GetContainer(), this, pComponentDataObject);
|
|
ASSERT(pHolder != NULL);
|
|
pHolder->SetSheetTitle(IDS_PROP_SHEET_TITLE_FMT, this);
|
|
return pHolder->CreateModelessSheet(lpProvider, handle);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Record Sorting in result pane
|
|
|
|
int FieldCompareHelper(CTreeNode* pNodeA, CTreeNode* pNodeB, int nCol)
|
|
{
|
|
int iRet = 0;
|
|
|
|
if (nCol == N_HEADER_NAME)
|
|
{
|
|
//
|
|
// If the name column is selected we have to sort PTR records by their
|
|
// address
|
|
//
|
|
CDNS_PTR_RecordNode* pRecNodeA = dynamic_cast<CDNS_PTR_RecordNode*>(pNodeA);
|
|
CDNS_PTR_RecordNode* pRecNodeB = dynamic_cast<CDNS_PTR_RecordNode*>(pNodeB);
|
|
|
|
if (pRecNodeA == NULL && pRecNodeB == NULL)
|
|
{
|
|
//
|
|
// Neither node is a PTR record, process normally
|
|
//
|
|
LPCTSTR lpszA = pNodeA->GetString(nCol);
|
|
LPCTSTR lpszB = pNodeB->GetString(nCol);
|
|
|
|
//
|
|
// cannot process NULL strings, have to use ""
|
|
//
|
|
if (lpszA == NULL || lpszB == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
return -1;
|
|
}
|
|
iRet = _wcsicmp(lpszA, lpszB);
|
|
}
|
|
else if (pRecNodeA == NULL)
|
|
{
|
|
//
|
|
// Push non PTR records down in the list
|
|
//
|
|
iRet = 1;
|
|
}
|
|
else if (pRecNodeB == NULL)
|
|
{
|
|
//
|
|
// Push non PTR records down in the list
|
|
//
|
|
iRet = -1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Both nodes are PTR records, compare their Addresses
|
|
// Subtract one from the other
|
|
// This will result in < 0 returned if the first one is less than the second
|
|
// 0 if they are equal, and > 0 if the first is greater than the second
|
|
//
|
|
LPCWSTR lpszNameA, lpszNameB;
|
|
lpszNameA = pRecNodeA->GetTrueRecordName();
|
|
lpszNameB = pRecNodeB->GetTrueRecordName();
|
|
|
|
if (lpszNameA == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (lpszNameB == NULL)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
DWORD dwAddrA, dwAddrB;
|
|
int iConverts = swscanf(lpszNameA, L"%d", &dwAddrA);
|
|
if (iConverts != 1)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
iConverts = swscanf(lpszNameB, L"%d", &dwAddrB);
|
|
if (iConverts != 1)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
iRet = dwAddrA - dwAddrB;
|
|
}
|
|
}
|
|
else if (nCol == N_HEADER_DATA)
|
|
{
|
|
//
|
|
// If the data column is selected we have to check the record type so
|
|
// that we can sort by IP address
|
|
//
|
|
CDNS_A_RecordNode* pRecNodeA = dynamic_cast<CDNS_A_RecordNode*>(pNodeA);
|
|
CDNS_A_RecordNode* pRecNodeB = dynamic_cast<CDNS_A_RecordNode*>(pNodeB);
|
|
|
|
if (pRecNodeA == NULL && pRecNodeB == NULL)
|
|
{
|
|
//
|
|
// Neither node is an A record, process normally
|
|
//
|
|
LPCTSTR lpszA = pNodeA->GetString(nCol);
|
|
LPCTSTR lpszB = pNodeB->GetString(nCol);
|
|
|
|
//
|
|
// cannot process NULL strings, have to use ""
|
|
//
|
|
if (lpszA == NULL || lpszB == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
return -1;
|
|
}
|
|
iRet = _wcsicmp(lpszA, lpszB);
|
|
}
|
|
else if (pRecNodeA == NULL)
|
|
{
|
|
//
|
|
// Push non A records down in the list
|
|
//
|
|
iRet = 1;
|
|
}
|
|
else if (pRecNodeB == NULL)
|
|
{
|
|
//
|
|
// Push non A records down in the list
|
|
//
|
|
iRet = -1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Both nodes are A records, compare their IP Addresses
|
|
// Subtract one from the other
|
|
// This will result in < 0 returned if the first one is less than the second
|
|
// 0 if they are equal, and > 0 if the first is greater than the second
|
|
//
|
|
DWORD dwIPA, dwIPB;
|
|
dwIPA = pRecNodeA->GetIPAddress();
|
|
dwIPB = pRecNodeB->GetIPAddress();
|
|
|
|
UINT nOctetCount = 0;
|
|
iRet = 0;
|
|
while (iRet == 0 && nOctetCount < 4)
|
|
{
|
|
iRet = (dwIPA & 0xff) - (dwIPB & 0xff);
|
|
dwIPA = dwIPA >> 8;
|
|
dwIPB = dwIPB >> 8;
|
|
++nOctetCount;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LPCTSTR lpszA = pNodeA->GetString(nCol);
|
|
LPCTSTR lpszB = pNodeB->GetString(nCol);
|
|
|
|
//
|
|
// cannot process NULL strings, have to use ""
|
|
//
|
|
if (lpszA == NULL || lpszB == NULL)
|
|
{
|
|
ASSERT(FALSE);
|
|
return -1;
|
|
}
|
|
iRet = _wcsicmp(lpszA, lpszB);
|
|
}
|
|
return iRet;
|
|
}
|
|
|
|
int CDNSDomainNode::Compare(CTreeNode* pNodeA, CTreeNode* pNodeB, int nCol, long)
|
|
{
|
|
// sorting rules for secondary fields
|
|
int nColSec = N_HEADER_TYPE;
|
|
int nColThird = N_HEADER_DATA;
|
|
switch (nCol)
|
|
{
|
|
case N_HEADER_NAME:
|
|
nColSec = N_HEADER_TYPE;
|
|
nColThird = N_HEADER_DATA;
|
|
break;
|
|
case N_HEADER_TYPE:
|
|
nColSec = N_HEADER_NAME;
|
|
nColThird = N_HEADER_DATA;
|
|
break;
|
|
case N_HEADER_DATA:
|
|
nColSec = N_HEADER_NAME;
|
|
nColThird = N_HEADER_TYPE;
|
|
break;
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
int nResult = FieldCompareHelper(pNodeA, pNodeB, nCol);
|
|
if (nResult != 0)
|
|
return nResult;
|
|
nResult = FieldCompareHelper(pNodeA, pNodeB, nColSec);
|
|
if (nResult != 0)
|
|
return nResult;
|
|
return FieldCompareHelper(pNodeA, pNodeB, nColThird);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// NS record bulk manipulation
|
|
|
|
|
|
|
|
// function to the user for confirmation on editing of A records
|
|
// associated with an NS record
|
|
BOOL _ConfirmEditAction(CDNSRecordNodeEditInfo* pInfo, BOOL bAsk)
|
|
{
|
|
if (!bAsk)
|
|
return TRUE; // silently do it
|
|
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
|
ASSERT(pInfo->m_pRecord->GetType() == DNS_TYPE_A);
|
|
ASSERT(pInfo->m_pRecord != NULL);
|
|
CDNS_A_RecordNode* pARecordNode = (CDNS_A_RecordNode*)pInfo->m_pRecordNode;
|
|
|
|
// load format message
|
|
CString szFmt;
|
|
szFmt.LoadString(IDS_MSG_RECORD_DEL_A_FROM_NS);
|
|
|
|
// compose message
|
|
CString szMsg;
|
|
szMsg.Format((LPCWSTR)szFmt, pARecordNode->GetString(0), pARecordNode->GetString(2));
|
|
|
|
return (IDYES == DNSMessageBox(szMsg, MB_YESNO | MB_ICONWARNING ) );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CDNSDomainNode::GetNSRecordNodesInfo(CDNSRecordNodeEditInfoList* pNSInfoList)
|
|
{
|
|
ASSERT(pNSInfoList != NULL);
|
|
if (!pNSInfoList->IsEmpty())
|
|
{
|
|
ASSERT(FALSE); // should never happen
|
|
pNSInfoList->RemoveAllNodes();
|
|
}
|
|
CDNS_NS_RecordNodeList* pNodeList = GetNSRecordNodeList();
|
|
|
|
// for each NS record in the list, create an entry in the info list
|
|
POSITION pos;
|
|
for( pos = pNodeList->GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CDNS_NS_RecordNode* pCurrNode = pNodeList->GetNext(pos);
|
|
ASSERT(pCurrNode != NULL);
|
|
CDNSRecordNodeEditInfo* pNSNodeInfo = new CDNSRecordNodeEditInfo();
|
|
if (pNSNodeInfo)
|
|
{
|
|
// set the data for the NS record, already in the list, so we do now own the memory
|
|
pNSNodeInfo->CreateFromExistingRecord(pCurrNode, FALSE /*bOwnMemory*/, TRUE /*bUpdateUI*/);
|
|
// for the current NS record, find the associated A records
|
|
FindARecordsFromNSInfo(pCurrNode->GetString(2),pNSNodeInfo->m_pEditInfoList);
|
|
pNSInfoList->AddTail(pNSNodeInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CDNSDomainNode::HasNSRecords()
|
|
{
|
|
return GetNSRecordNodeList()->GetCount() > 0;
|
|
}
|
|
|
|
BOOL CDNSDomainNode::UpdateNSRecordNodesInfo(CDNSRecordNodeEditInfoList* pNewInfoList,
|
|
CComponentDataObject* pComponentData)
|
|
{
|
|
ASSERT(pNewInfoList != NULL);
|
|
|
|
// return false if at least one operation failed
|
|
BOOL bRes = TRUE;
|
|
CDNS_NS_RecordNodeList* pNSRecordNodeList = GetNSRecordNodeList();
|
|
|
|
// clear the current state in this domain object
|
|
pNSRecordNodeList->RemoveAll();
|
|
|
|
// rebuild the current list from the new one, while applying the changes
|
|
POSITION pos;
|
|
for ( pos = pNewInfoList->GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CDNSRecordNodeEditInfo* pCurrentInfo = pNewInfoList->GetNext(pos);
|
|
ASSERT(pCurrentInfo->m_pRecordNode != NULL);
|
|
ASSERT(pCurrentInfo->m_pRecord != NULL);
|
|
switch (pCurrentInfo->m_action)
|
|
{
|
|
case CDNSRecordNodeEditInfo::add:
|
|
case CDNSRecordNodeEditInfo::edit:
|
|
{
|
|
if (pCurrentInfo->Update(this, pComponentData) == 0)
|
|
{
|
|
ASSERT(pCurrentInfo->m_pRecordNode->GetType() == DNS_TYPE_NS);
|
|
pNSRecordNodeList->AddTail((CDNS_NS_RecordNode*)pCurrentInfo->m_pRecordNode);
|
|
pCurrentInfo->m_bOwnMemory = FALSE; // relinquish ownership
|
|
}
|
|
else
|
|
{
|
|
bRes = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case CDNSRecordNodeEditInfo::remove:
|
|
{
|
|
if (pCurrentInfo->m_bExisting)
|
|
{
|
|
if (pCurrentInfo->Remove(this, pComponentData) != 0)
|
|
bRes = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CDNSRecordNodeEditInfo::none:
|
|
//
|
|
// Do nothing if the the node has been added and then removed without having been applied
|
|
//
|
|
break;
|
|
|
|
default:
|
|
{
|
|
ASSERT(pCurrentInfo->m_bOwnMemory == FALSE);
|
|
ASSERT(pCurrentInfo->m_action == CDNSRecordNodeEditInfo::unchanged);
|
|
|
|
//
|
|
// We still have to update the NS record because the server needs to
|
|
// update the record in memory (bug 23905)
|
|
//
|
|
if (pCurrentInfo->Update(this, pComponentData) == 0)
|
|
{
|
|
pNSRecordNodeList->AddTail((CDNS_NS_RecordNode*)pCurrentInfo->m_pRecordNode);
|
|
}
|
|
}
|
|
}; // switch
|
|
// now we have to apply the changes in the list of A records
|
|
if (pCurrentInfo->m_dwErr == 0 && pCurrentInfo->m_action != CDNSRecordNodeEditInfo::none)
|
|
UpdateARecordsOfNSInfo(pCurrentInfo, pComponentData);
|
|
} // for
|
|
|
|
return bRes;
|
|
}
|
|
|
|
// static function
|
|
void CDNSDomainNode::UpdateARecordsOfNSInfoHelper(CDNSDomainNode* pDomainNode,
|
|
CDNSRecordNodeEditInfo* pNSInfo,
|
|
CComponentDataObject* pComponentData,
|
|
BOOL bAskConfirmation)
|
|
{
|
|
ASSERT(pNSInfo->m_dwErr == 0);
|
|
ASSERT(pNSInfo->m_pRecordNode != NULL);
|
|
ASSERT(pNSInfo->m_pRecordNode->GetType() == DNS_TYPE_NS);
|
|
POSITION pos;
|
|
|
|
// get the list of related A records
|
|
CDNSRecordNodeEditInfoList* pNSInfoList = pNSInfo->m_pEditInfoList;
|
|
|
|
for( pos = pNSInfoList->GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CDNSRecordNodeEditInfo* pCurrentInfo = pNSInfoList->GetNext(pos);
|
|
ASSERT(pCurrentInfo->m_pRecordNode != NULL);
|
|
ASSERT(pCurrentInfo->m_pRecord != NULL);
|
|
CDNS_A_RecordNode* pARecordNode = (CDNS_A_RecordNode*)pCurrentInfo->m_pRecordNode;
|
|
ASSERT(pNSInfo->m_pRecord != NULL);
|
|
CDNS_NS_Record* pNSRecord = (CDNS_NS_Record*)pNSInfo->m_pRecord;
|
|
|
|
BOOL bHostNameChanged = !_match(pNSRecord->m_szNameNode, pARecordNode);
|
|
if (bHostNameChanged)
|
|
{
|
|
// the NS record points to a different host, so need
|
|
// to delete the old A RR and create a new one
|
|
BOOL bRemoveOld = _ConfirmEditAction(pCurrentInfo, bAskConfirmation);
|
|
|
|
CDNSRecordNodeEditInfo::actionType oldAction = pCurrentInfo->m_action;
|
|
if (pCurrentInfo->m_bExisting && bRemoveOld)
|
|
{
|
|
// if the A record was an existing one, need to remove first
|
|
pCurrentInfo->m_action = CDNSRecordNodeEditInfo::remove;
|
|
pCurrentInfo->Remove(pDomainNode, pComponentData);
|
|
}
|
|
// now decide if have to add
|
|
if (oldAction == CDNSRecordNodeEditInfo::remove && bRemoveOld)
|
|
{
|
|
// it was meant to be removed anyway
|
|
pCurrentInfo->m_bOwnMemory = TRUE; // edit info will clean up memory
|
|
}
|
|
else
|
|
{
|
|
// it was meant to be edited or added, restore old action code
|
|
pCurrentInfo->m_action = oldAction;
|
|
// change the name of the record
|
|
pCurrentInfo->m_pRecordNode->SetRecordName(pNSRecord->m_szNameNode, FALSE /*bAtTheNode*/);
|
|
// add new A record with different FQDN
|
|
pCurrentInfo->m_action = CDNSRecordNodeEditInfo::add;
|
|
pCurrentInfo->Update(pDomainNode, pComponentData);
|
|
pCurrentInfo->m_bOwnMemory = FALSE; // written im master structures
|
|
}
|
|
|
|
}
|
|
else // the name is still the same
|
|
{
|
|
switch(pNSInfo->m_action)
|
|
{
|
|
case CDNSRecordNodeEditInfo::remove:
|
|
{
|
|
// NS record marked for deletion means removing the associated A records
|
|
if (pCurrentInfo->m_bExisting && _ConfirmEditAction(pCurrentInfo, bAskConfirmation))
|
|
{
|
|
pCurrentInfo->Remove(pDomainNode, pComponentData);
|
|
pCurrentInfo->m_bOwnMemory = TRUE; // it will cleanup itself
|
|
}
|
|
|
|
}
|
|
break;
|
|
case CDNSRecordNodeEditInfo::add:
|
|
{
|
|
if (!pCurrentInfo->m_bExisting)
|
|
{
|
|
pCurrentInfo->Update(pDomainNode, pComponentData);
|
|
pCurrentInfo->m_bOwnMemory = FALSE; // written im master structures
|
|
}
|
|
}
|
|
break;
|
|
case CDNSRecordNodeEditInfo::edit:
|
|
{
|
|
// NS host name not changed, just update list of A records
|
|
switch(pCurrentInfo->m_action)
|
|
{
|
|
case CDNSRecordNodeEditInfo::remove:
|
|
{
|
|
if (pCurrentInfo->m_bExisting && _ConfirmEditAction(pCurrentInfo, bAskConfirmation))
|
|
{
|
|
pCurrentInfo->Remove(pDomainNode, pComponentData);
|
|
pCurrentInfo->m_bOwnMemory = TRUE; // it will cleanup itself
|
|
}
|
|
}
|
|
break;
|
|
case CDNSRecordNodeEditInfo::edit:
|
|
{
|
|
// we just changed the TTL
|
|
ASSERT(pCurrentInfo->m_bExisting);
|
|
pCurrentInfo->Update(pDomainNode, pComponentData);
|
|
pCurrentInfo->m_bOwnMemory = FALSE; // written im master structures
|
|
}
|
|
break;
|
|
case CDNSRecordNodeEditInfo::add:
|
|
{
|
|
if (!pCurrentInfo->m_bExisting)
|
|
{
|
|
pCurrentInfo->Update(pDomainNode, pComponentData);
|
|
pCurrentInfo->m_bOwnMemory = FALSE; // written im master structures
|
|
}
|
|
}
|
|
break;
|
|
}; // switch
|
|
}
|
|
break;
|
|
}; // switch
|
|
} // if,else
|
|
} // for
|
|
|
|
}
|
|
|
|
|
|
void CDNSDomainNode::UpdateARecordsOfNSInfo(CDNSRecordNodeEditInfo* pNSInfo,
|
|
CComponentDataObject* pComponentData)
|
|
{
|
|
// create a fake domain object to run a query looking for
|
|
// A records that match the given list of NS records
|
|
CDNSDummyDomainNode fakeDomain;
|
|
fakeDomain.SetServerNode(GetServerNode());
|
|
fakeDomain.SetZone(GetZoneNode());
|
|
BOOL bAskConfirmation = TRUE; // we migth delete A RR's that we need
|
|
UpdateARecordsOfNSInfoHelper(&fakeDomain, pNSInfo, pComponentData, bAskConfirmation);
|
|
}
|
|
|
|
|
|
|
|
void CDNSDomainNode::FindARecordsFromNSInfo(LPCTSTR lpszNSName,
|
|
CDNSRecordNodeEditInfoList* pNSInfoList)
|
|
{
|
|
// just call the static version
|
|
CDNSRootData* pRootData = (CDNSRootData*)GetRootContainer();
|
|
ASSERT(pRootData != NULL);
|
|
|
|
DWORD cAddrCount;
|
|
PIP_ADDRESS pipAddrs;
|
|
GetServerNode()->GetListenAddressesInfo(&cAddrCount, &pipAddrs);
|
|
if (cAddrCount == 0)
|
|
{
|
|
// listening on all addresses
|
|
GetServerNode()->GetServerAddressesInfo(&cAddrCount, &pipAddrs);
|
|
}
|
|
|
|
FindARecordsFromNSInfo(GetServerNode()->GetRPCName(),
|
|
GetServerNode()->GetVersion(),
|
|
cAddrCount, pipAddrs,
|
|
GetZoneNode()->GetFullName(),
|
|
lpszNSName,
|
|
pNSInfoList,
|
|
pRootData->IsAdvancedView());
|
|
}
|
|
|
|
void CDNSDomainNode::FindARecordsFromNSInfo(LPCWSTR lpszServerName, DWORD dwServerVersion,
|
|
DWORD cServerAddrCount, PIP_ADDRESS pipServerAddrs,
|
|
LPCWSTR lpszZoneName,
|
|
LPCWSTR lpszNSName,
|
|
CDNSRecordNodeEditInfoList* pNSInfoList,
|
|
BOOL bAdvancedView)
|
|
{
|
|
ASSERT(pNSInfoList != NULL);
|
|
ASSERT(pNSInfoList->IsEmpty());
|
|
|
|
// specifically look for A records matching a given NS name
|
|
|
|
// set query flags to get all the possible data
|
|
DWORD dwSelectFlags = DNS_RPC_VIEW_AUTHORITY_DATA | DNS_RPC_VIEW_GLUE_DATA |
|
|
DNS_RPC_VIEW_ADDITIONAL_DATA;
|
|
|
|
CDNSDomainQueryObj query(lpszServerName,
|
|
lpszZoneName,
|
|
dwServerVersion,
|
|
NULL, // lpszNodeName, no need here
|
|
lpszNSName,
|
|
DNS_TYPE_A,
|
|
dwSelectFlags,
|
|
FALSE, // zone
|
|
FALSE, // reverse
|
|
FALSE, // cache
|
|
bAdvancedView);
|
|
query.Enumerate();
|
|
|
|
// get record from the queue into the info
|
|
CObjBaseList* pChildList = query.GetQueue();
|
|
//int n = pChildList->GetCount();
|
|
while (!pChildList->IsEmpty())
|
|
{
|
|
CTreeNode* pNode = dynamic_cast<CTreeNode*>(pChildList->RemoveHead());
|
|
ASSERT(pNode != NULL);
|
|
if (!pNode->IsContainer())
|
|
{
|
|
CDNSRecordNodeBase* pRec = (CDNSRecordNodeBase*)pNode;
|
|
if (pRec->GetType() == DNS_TYPE_A)
|
|
{
|
|
TRACE(_T("Record <%s>\n"), pRec->GetString(2));
|
|
pRec->SetRecordName(lpszNSName, FALSE /*bAtTheNode*/);
|
|
CDNSRecordNodeEditInfo* pANodeInfo = new CDNSRecordNodeEditInfo;
|
|
if (pANodeInfo)
|
|
{
|
|
// NOTICE: we assume that all the nodes are glue, so we own the memory
|
|
pANodeInfo->CreateFromExistingRecord(pRec, TRUE /*bOwnMemory*/, FALSE /*bUpdateUI*/);
|
|
pNSInfoList->AddTail(pANodeInfo);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
delete pNode; // discard
|
|
}
|
|
|
|
if (pNSInfoList->GetCount() > 0)
|
|
return; // got the info we needed just using RPC
|
|
|
|
// Could not find the A records, we need to try DnsQuery to get info outside the server
|
|
|
|
// search using DnsQuery and convert
|
|
PDNS_RECORD pDnsQueryARecordList = NULL;
|
|
|
|
// if available, use the provided addresses to do a DnsQuery()
|
|
PIP_ARRAY pipArr = NULL;
|
|
if ( (cServerAddrCount > 0) && (pipServerAddrs != NULL) )
|
|
{
|
|
pipArr = (PIP_ARRAY)malloc(sizeof(DWORD)+sizeof(IP_ADDRESS)*cServerAddrCount);
|
|
if (!pipArr)
|
|
{
|
|
return;
|
|
}
|
|
pipArr->AddrCount = cServerAddrCount;
|
|
memcpy(pipArr->AddrArray, pipServerAddrs, sizeof(IP_ADDRESS)*cServerAddrCount);
|
|
}
|
|
|
|
DWORD dwErr = ::DnsQuery((LPWSTR)lpszNSName, DNS_TYPE_A,
|
|
DNS_QUERY_NO_RECURSION, pipArr, &pDnsQueryARecordList, NULL);
|
|
|
|
if (pipArr)
|
|
{
|
|
free(pipArr);
|
|
pipArr = 0;
|
|
}
|
|
|
|
// no luck, try a simple query, with no IP addresses specified
|
|
|
|
if (pDnsQueryARecordList == NULL)
|
|
{
|
|
dwErr = ::DnsQuery((LPWSTR)lpszNSName, DNS_TYPE_A,
|
|
DNS_QUERY_NO_RECURSION, NULL, &pDnsQueryARecordList, NULL);
|
|
}
|
|
|
|
if (pDnsQueryARecordList == NULL)
|
|
return; // failed, no way to resolve IP address
|
|
|
|
PDNS_RECORD pCurrDnsQueryRecord = pDnsQueryARecordList;
|
|
while (pCurrDnsQueryRecord)
|
|
{
|
|
if (pCurrDnsQueryRecord->Flags.S.Section == DNSREC_ANSWER)
|
|
{
|
|
if (pCurrDnsQueryRecord->wType == DNS_TYPE_A)
|
|
{
|
|
// create a record node
|
|
CDNSRecordNodeBase* pRecordNode =
|
|
CDNSRecordInfo::CreateRecordNode(pCurrDnsQueryRecord->wType);
|
|
pRecordNode->CreateFromDnsQueryRecord(pCurrDnsQueryRecord, 0x0);
|
|
|
|
pRecordNode->SetRecordName(lpszNSName, FALSE /*bAtTheNode*/);
|
|
CDNSRecordNodeEditInfo* pANodeInfo = new CDNSRecordNodeEditInfo;
|
|
if (pANodeInfo)
|
|
{
|
|
pANodeInfo->m_bFromDnsQuery = TRUE;
|
|
|
|
//
|
|
// NOTICE: we assume that all the nodes are glue, so we own the memory
|
|
//
|
|
pANodeInfo->CreateFromExistingRecord(pRecordNode, TRUE /*bOwnMemory*/, FALSE /*bUpdateUI*/);
|
|
pNSInfoList->AddTail(pANodeInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// goto next record
|
|
pCurrDnsQueryRecord = pCurrDnsQueryRecord->pNext;
|
|
}
|
|
|
|
DnsRecordListFree(pDnsQueryARecordList, DnsFreeRecordListDeep);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DNS_STATUS CDNSDomainNode::EnumerateNodes(LPCTSTR lpszServerName,
|
|
LPCTSTR lpszZoneName,
|
|
LPCTSTR lpszNodeName,
|
|
LPCTSTR lpszFullNodeName,
|
|
WORD wRecordType,
|
|
DWORD dwSelectFlag,
|
|
BOOL, BOOL bReverse, BOOL bAdvancedView,
|
|
CDNSDomainQueryObj* pQuery)
|
|
{
|
|
ASSERT(pQuery != NULL);
|
|
USES_CONVERSION;
|
|
DNS_STATUS err = 0;
|
|
CHAR szStartChildAnsi[3*MAX_DNS_NAME_LEN + 1]; // can have multibyte chars, count NULL
|
|
szStartChildAnsi[0] = NULL;
|
|
WCHAR szStartChild[MAX_DNS_NAME_LEN + 1]; // count NULL
|
|
szStartChild[0] = NULL;
|
|
|
|
CTreeNode* pNodeToInsert = NULL; // delayed insert
|
|
CDNSRecordNodeBase* pMoreDataNode = NULL;
|
|
|
|
// convert to UTF8 names
|
|
LPCSTR lpszFullNodeNameAnsi = W_TO_UTF8(lpszFullNodeName);
|
|
LPCSTR lpszZoneNameAnsi = W_TO_UTF8(lpszZoneName);
|
|
|
|
BOOL bTooMuchData = FALSE;
|
|
|
|
do // while more data
|
|
{
|
|
// get a chunk of data from RPC call
|
|
BYTE* pbRpcBuffer = NULL;
|
|
DWORD cbRpcBufferUsed = 0;
|
|
|
|
err = ::DnssrvEnumRecords(lpszServerName,
|
|
lpszZoneNameAnsi,
|
|
lpszFullNodeNameAnsi, // e.g. "foo.bar.com."
|
|
szStartChildAnsi, // Start Child
|
|
wRecordType,
|
|
dwSelectFlag,
|
|
NULL, // pszFilterStart
|
|
NULL, // pszFilterStop
|
|
&cbRpcBufferUsed,
|
|
&pbRpcBuffer);
|
|
|
|
if ((err != ERROR_MORE_DATA) && (err != 0))
|
|
return err; // bail out if there is an error
|
|
|
|
// walk the memory and build objects
|
|
DNS_RPC_NODE * pDnsNode = (DNS_RPC_NODE *)pbRpcBuffer;
|
|
DNS_RPC_RECORD * pDnsRecord;
|
|
void* pvEndOfRpcBuffer = pbRpcBuffer + cbRpcBufferUsed;
|
|
while ( (!bTooMuchData) && (pDnsNode < pvEndOfRpcBuffer) )
|
|
{
|
|
// get an ANSI null terminated copy
|
|
memcpy(szStartChildAnsi, pDnsNode->dnsNodeName.achName, pDnsNode->dnsNodeName.cchNameLength);
|
|
szStartChildAnsi[pDnsNode->dnsNodeName.cchNameLength] = NULL;
|
|
|
|
//
|
|
// get a UNICODE null terminated copy
|
|
//
|
|
if (szStartChildAnsi[0] == NULL)
|
|
{
|
|
szStartChild[0] = NULL;
|
|
}
|
|
else
|
|
{
|
|
DnsUtf8ToWHelper(szStartChild, szStartChildAnsi, pDnsNode->dnsNodeName.cchNameLength+1);
|
|
}
|
|
|
|
if (pDnsNode->dwChildCount || (pDnsNode->dwFlags & DNS_RPC_NODE_FLAG_STICKY))
|
|
{
|
|
BOOL bDelegation = ( ((dwSelectFlag & DNS_RPC_VIEW_CACHE_DATA) == 0) &&
|
|
((pDnsNode->dwFlags & DNS_RPC_FLAG_ZONE_ROOT) != 0) );
|
|
CDNSDomainNode* p = NULL;
|
|
if (pQuery->CanAddDomain(szStartChild))
|
|
{
|
|
bTooMuchData = pQuery->TooMuchData();
|
|
if (!bTooMuchData)
|
|
{
|
|
p = new CDNSDomainNode(bDelegation);
|
|
p->SetNames(FALSE, bReverse, bAdvancedView, szStartChild, lpszFullNodeName);
|
|
}
|
|
}
|
|
if (pNodeToInsert != NULL)
|
|
{
|
|
VERIFY(pQuery->AddQueryResult(pNodeToInsert));
|
|
}
|
|
pNodeToInsert = p;
|
|
}
|
|
pDnsRecord = (DNS_RPC_RECORD *)((BYTE *)pDnsNode + NEXT_DWORD(pDnsNode->wLength));
|
|
ASSERT(IS_DWORD_ALIGNED(pDnsRecord));
|
|
|
|
//
|
|
// Add the records under that node
|
|
//
|
|
UINT cRecordCount = pDnsNode->wRecordCount;
|
|
while ( (!bTooMuchData) && (cRecordCount--) )
|
|
{
|
|
CDNSRecordNodeBase* p = NULL;
|
|
BOOL bAtTheNode = szStartChild[0] == NULL;
|
|
LPCWSTR lpszRecordName = (bAtTheNode) ? lpszNodeName : szStartChild;
|
|
if (pQuery->CanAddRecord(pDnsRecord->wType, lpszRecordName))
|
|
{
|
|
TRACE(_T("\tCan add record %ws\n"), lpszRecordName);
|
|
bTooMuchData = pQuery->TooMuchData();
|
|
if (!bTooMuchData)
|
|
{
|
|
if (bAtTheNode)
|
|
{
|
|
p = CDNSRecordInfo::CreateRecordNodeFromRPCData(
|
|
lpszRecordName, pDnsRecord,bAtTheNode);
|
|
}
|
|
else
|
|
{
|
|
// filter out the NS records that are not at the node
|
|
if (pDnsRecord->wType != DNS_TYPE_NS)
|
|
{
|
|
p = CDNSRecordInfo::CreateRecordNodeFromRPCData(
|
|
lpszRecordName, pDnsRecord,bAtTheNode);
|
|
}
|
|
}
|
|
} // if not too much data
|
|
} // if can add
|
|
|
|
if (p != NULL)
|
|
{
|
|
p->SetFlagsDown(TN_FLAG_DNS_RECORD_FULL_NAME, !bAdvancedView);
|
|
if (pNodeToInsert != NULL)
|
|
{
|
|
VERIFY(pQuery->AddQueryResult(pNodeToInsert));
|
|
}
|
|
|
|
if (pMoreDataNode != NULL)
|
|
{
|
|
//
|
|
// If there was more data check to see if the new node is the same as the
|
|
// last node from the previous batch. Insert it if they are different, delete
|
|
// it if they are not
|
|
//
|
|
CString szMoreDataName = pMoreDataNode->GetDisplayName();
|
|
CString szPName = p->GetDisplayName();
|
|
|
|
if (szMoreDataName == szPName &&
|
|
pMoreDataNode->GetType() == p->GetType() &&
|
|
_wcsicmp(pMoreDataNode->GetString(3), p->GetString(3)) == 0)
|
|
{
|
|
delete pMoreDataNode;
|
|
}
|
|
else
|
|
{
|
|
VERIFY(pQuery->AddQueryResult(pMoreDataNode));
|
|
}
|
|
pMoreDataNode = NULL;
|
|
}
|
|
pNodeToInsert = p;
|
|
}
|
|
pDnsRecord = DNS_NEXT_RECORD(pDnsRecord);
|
|
} // while cRecordCount
|
|
|
|
// The new node is found at the end of the last record
|
|
pDnsNode = (DNS_RPC_NODE *)pDnsRecord;
|
|
} // while end of buffer
|
|
|
|
|
|
// we still have a node to insert, but we discard it if there is more data
|
|
// because we are going to get it again and we want to avoid duplication
|
|
if (pNodeToInsert != NULL)
|
|
{
|
|
if (bTooMuchData)
|
|
{
|
|
delete pNodeToInsert;
|
|
}
|
|
else if (err == ERROR_MORE_DATA)
|
|
{
|
|
//
|
|
// Doesn't matter if this turns out NULL because we only want
|
|
// pMoreDataNode to be a record node. If its a domain node we
|
|
// can just ignore it
|
|
//
|
|
pMoreDataNode = dynamic_cast<CDNSRecordNodeBase*>(pNodeToInsert);
|
|
}
|
|
else
|
|
{
|
|
VERIFY(pQuery->AddQueryResult(pNodeToInsert));
|
|
}
|
|
pNodeToInsert = NULL;
|
|
}
|
|
|
|
::DnssrvFreeRecordsBuffer(pbRpcBuffer);
|
|
|
|
} while ( !bTooMuchData && (err == ERROR_MORE_DATA) ) ;
|
|
|
|
// we are bailing out because of too much data,
|
|
// need to let the main tread know
|
|
if (bTooMuchData && (err != ERROR_MORE_DATA))
|
|
{
|
|
err = ERROR_MORE_DATA;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
DNS_STATUS CDNSDomainNode::Create()
|
|
{
|
|
USES_CONVERSION;
|
|
LPCWSTR lpszFullZoneName = NULL;
|
|
|
|
CDNSZoneNode* pZoneNode = GetZoneNode();
|
|
|
|
if (pZoneNode != NULL)
|
|
lpszFullZoneName = pZoneNode->GetFullName();
|
|
DNS_STATUS err = ::DnssrvUpdateRecord(GetServerNode()->GetRPCName(),
|
|
W_TO_UTF8(lpszFullZoneName),
|
|
W_TO_UTF8(GetFullName()),
|
|
NULL, NULL);
|
|
return err;
|
|
}
|
|
|
|
DNS_STATUS CDNSDomainNode::Delete()
|
|
{
|
|
USES_CONVERSION;
|
|
LPCWSTR lpszFullZoneName = NULL;
|
|
CDNSZoneNode* pZoneNode = GetZoneNode();
|
|
if (pZoneNode != NULL)
|
|
lpszFullZoneName = pZoneNode->GetFullName();
|
|
|
|
return ::DnssrvDeleteNode(GetServerNode()->GetRPCName(),
|
|
W_TO_UTF8(lpszFullZoneName),
|
|
W_TO_UTF8(GetFullName()),
|
|
TRUE // fDeleteSubtree
|
|
);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// CDNSRootHintsNode
|
|
|
|
|
|
|
|
DNS_STATUS CDNSRootHintsNode::QueryForRootHints(LPCTSTR lpszServerName, DWORD dwServerVersion)
|
|
{
|
|
USES_CONVERSION;
|
|
DWORD dwSelectFlags = DNS_RPC_VIEW_ROOT_HINT_DATA | DNS_RPC_VIEW_ADDITIONAL_DATA | DNS_RPC_VIEW_NO_CHILDREN;
|
|
CDNSDomainQueryObj query(lpszServerName,
|
|
UTF8_TO_W(DNS_ZONE_ROOT_HINTS), //lpszZoneName, needs to be "..RootHints" as defined in dnsrpc.h
|
|
dwServerVersion,
|
|
GetDisplayName(),
|
|
m_szFullName,
|
|
DNS_TYPE_NS,
|
|
dwSelectFlags,
|
|
FALSE, // zone
|
|
FALSE, // reverse
|
|
FALSE, // cache
|
|
FALSE);
|
|
query.Enumerate();
|
|
DWORD dwErr = query.GetError();
|
|
if (dwErr != 0)
|
|
return dwErr;
|
|
// get record from the queue into the folder
|
|
CObjBaseList* pChildList = query.GetQueue();
|
|
//int n = pChildList->GetCount();
|
|
while (!pChildList->IsEmpty())
|
|
{
|
|
CTreeNode* pNode = dynamic_cast<CTreeNode*>(pChildList->RemoveHead());
|
|
// NOTICE: for NT 4.0 servers, we get bogus container nodes
|
|
// that we have to suppress
|
|
if(pNode->IsContainer())
|
|
{
|
|
delete pNode;
|
|
}
|
|
else
|
|
{
|
|
OnHaveRecord((CDNSRecordNodeBase*)pNode, NULL); // add to the list of NS records
|
|
AddChildToList(pNode);
|
|
}
|
|
}
|
|
return (DNS_STATUS)dwErr;
|
|
}
|
|
|
|
|
|
void CDNSRootHintsNode::FindARecordsFromNSInfo(LPCTSTR lpszNSName,
|
|
CDNSRecordNodeEditInfoList* pNSInfoList)
|
|
{
|
|
ASSERT(pNSInfoList != NULL);
|
|
|
|
//
|
|
// for root hints, we have all the records in this folder and we
|
|
// can edit them
|
|
//
|
|
|
|
POSITION pos;
|
|
for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos);
|
|
ASSERT(!pCurrentChild->IsContainer());
|
|
CDNSRecordNodeBase* pRecordNode = (CDNSRecordNodeBase*)pCurrentChild;
|
|
if (DNS_TYPE_A == pRecordNode->GetType())
|
|
{
|
|
CDNS_A_RecordNode* pARecordNode = (CDNS_A_RecordNode*)pRecordNode;
|
|
if (_match(lpszNSName, pARecordNode))
|
|
{
|
|
CDNSRecordNodeEditInfo* pANodeInfo = new CDNSRecordNodeEditInfo;
|
|
|
|
//
|
|
// NOTICE: the root hints folder owns the memory
|
|
//
|
|
if (pANodeInfo != NULL)
|
|
{
|
|
pANodeInfo->CreateFromExistingRecord(pARecordNode, FALSE /*bOwnMemory*/, TRUE /*bUpdateUI*/);
|
|
pNSInfoList->AddTail(pANodeInfo);
|
|
}
|
|
else
|
|
{
|
|
TRACE(_T("Failed to allocate memory in CDNSRootHintsNode::FindARecordsFromNSInfo"));
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDNSRootHintsNode::UpdateARecordsOfNSInfo(CDNSRecordNodeEditInfo* pNSInfo,
|
|
CComponentDataObject* pComponentData)
|
|
{
|
|
BOOL bAskConfirmation = FALSE; // need to edit ALL A records
|
|
UpdateARecordsOfNSInfoHelper(this, pNSInfo, pComponentData, bAskConfirmation);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DNS_STATUS CDNSRootHintsNode::Clear()
|
|
{
|
|
//
|
|
// clear the list of cached NS record pointers
|
|
//
|
|
GetNSRecordNodeList()->RemoveAll();
|
|
|
|
//
|
|
// remove all the records from the server
|
|
//
|
|
DNS_STATUS err = 0;
|
|
POSITION pos;
|
|
|
|
for( pos = m_leafChildList.GetHeadPosition(); pos != NULL; )
|
|
{
|
|
CTreeNode* pCurrentChild = m_leafChildList.GetNext(pos);
|
|
ASSERT(!pCurrentChild->IsContainer());
|
|
CDNSRecordNodeBase* pRecordNode = (CDNSRecordNodeBase*)pCurrentChild;
|
|
DNS_STATUS currErr = pRecordNode->DeleteOnServer();
|
|
if (currErr != 0)
|
|
{
|
|
//
|
|
// just ge the last error, if any
|
|
//
|
|
err = currErr;
|
|
}
|
|
}
|
|
//
|
|
// clear the list of children in the folder (we are hidden, so no UI deletions)
|
|
//
|
|
RemoveAllChildrenFromList();
|
|
return err;
|
|
}
|
|
|
|
|
|
DNS_STATUS CDNSRootHintsNode::InitializeFromDnsQueryData(PDNS_RECORD pRootHintsRecordList)
|
|
{
|
|
// need to remove all the previous root hints from the server
|
|
// let's be sure we get recent data
|
|
|
|
// clear the list of children in the folder (we are hidden, so no UI deletions)
|
|
RemoveAllChildrenFromList();
|
|
// acqure the list of current root hints
|
|
CDNSServerNode* pServerNode = GetServerNode();
|
|
DNS_STATUS dwErr = QueryForRootHints(pServerNode->GetRPCName(), pServerNode->GetVersion());
|
|
if (dwErr != 0)
|
|
{
|
|
TRACE(_T("Failed to remove old Root Hints, dwErr = %x hex\n"), dwErr);
|
|
return dwErr;
|
|
}
|
|
|
|
// remove all the old root hints from server and client side
|
|
dwErr = Clear();
|
|
if (dwErr != 0)
|
|
{
|
|
TRACE(_T("Failed to clear Root Hints, dwErr = %x hex\n"), dwErr);
|
|
return dwErr;
|
|
}
|
|
|
|
// walk through the list of root hints,
|
|
// convert to C++ format,
|
|
// write to server and add to the folder list (no UI, folder hidden)
|
|
PDNS_RECORD pCurrDnsQueryRecord = pRootHintsRecordList;
|
|
while (pCurrDnsQueryRecord != NULL)
|
|
{
|
|
ASSERT( (pCurrDnsQueryRecord->wType == DNS_TYPE_A) ||
|
|
(pCurrDnsQueryRecord->wType == DNS_TYPE_NS) );
|
|
// create a record node and read data from DnsQuery format
|
|
CDNSRecordNodeBase* pRecordNode =
|
|
CDNSRecordInfo::CreateRecordNode(pCurrDnsQueryRecord->wType);
|
|
pRecordNode->CreateFromDnsQueryRecord(pCurrDnsQueryRecord, DNS_RPC_RECORD_FLAG_ZONE_ROOT);
|
|
|
|
// set the record node container
|
|
pRecordNode->SetContainer(this);
|
|
|
|
// set the record node name
|
|
BOOL bAtTheNode = (pCurrDnsQueryRecord->wType == DNS_TYPE_NS);
|
|
pRecordNode->SetRecordName(pCurrDnsQueryRecord->pName, bAtTheNode);
|
|
|
|
// write on server
|
|
// the default TTL does not apply here
|
|
DNS_STATUS err = pRecordNode->Update(NULL, FALSE); // NULL = create new, FALSE = use def TTL
|
|
if (err == 0)
|
|
VERIFY(AddChildToList(pRecordNode));
|
|
else
|
|
{
|
|
dwErr = err; // mark las error
|
|
delete pRecordNode; // something went wrong
|
|
}
|
|
pCurrDnsQueryRecord = pCurrDnsQueryRecord->pNext;
|
|
}
|
|
|
|
// force a write on the server, to make sure the cache file is written right away
|
|
return CDNSZoneNode::WriteToDatabase(pServerNode->GetRPCName(), DNS_ZONE_ROOT_HINTS);
|
|
}
|
|
|