You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1242 lines
31 KiB
1242 lines
31 KiB
//++
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1987 - 1999
|
|
//
|
|
// Module Name:
|
|
//
|
|
// dcutil.c
|
|
//
|
|
// Abstract:
|
|
//
|
|
// Test to ensure that a workstation has network (IP) connectivity to
|
|
// the outside.
|
|
//
|
|
// Author:
|
|
//
|
|
// 15-Dec-1997 (cliffv)
|
|
// Anilth - 4-20-1998
|
|
//
|
|
// Environment:
|
|
//
|
|
// User mode only.
|
|
// Contains NT-specific code.
|
|
//
|
|
// Revision History:
|
|
//
|
|
// 1-June-1998 (denisemi) add DnsServerHasDCRecords to check DC dns records
|
|
// registration
|
|
//
|
|
// 26-June-1998 (t-rajkup) add general tcp/ip , dhcp and routing,
|
|
// winsock, ipx, wins and netbt information.
|
|
//--
|
|
|
|
//
|
|
// Common include files.
|
|
//
|
|
#include "precomp.h"
|
|
#include <iphlpint.h>
|
|
#include "dcutil.h"
|
|
#include "ipcfgtest.h"
|
|
|
|
DWORD CheckDomainConfig(IN PWSTR pwzDomainName, OUT PLIST_ENTRY plmsgOutput);
|
|
DWORD CheckAdapterDnsConfig( OUT PLIST_ENTRY plmsgOutput);
|
|
DWORD DnsDcSrvCheck(PWSTR pwzDnsDomain, OUT PLIST_ENTRY plmsgOutput);
|
|
DWORD ValidateDnsDomainName(PWSTR pwzDnsDomain, OUT PLIST_ENTRY plmsgOutput);
|
|
PWSTR ConcatonateStrings(PWSTR pwzFirst, PWSTR pwzSecond);
|
|
BOOL AddToList(PWSTR * ppwzList, PWSTR pwz);
|
|
BOOL BuildDomainList(PWSTR * ppwzDomainList, PWSTR pwzDnsDomain);
|
|
PWSTR AllocString(PWSTR pwz);
|
|
DWORD GetInterfacesStr( PWSTR *ppwIfStr);
|
|
|
|
const PWSTR g_pwzSrvRecordPrefix = L"_ldap._tcp.dc._msdcs.";
|
|
|
|
//(nsun) DC related routines
|
|
|
|
PTESTED_DC
|
|
GetUpTestedDc(
|
|
IN PTESTED_DOMAIN TestedDomain
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a DC that's currently up and running.
|
|
|
|
Arguments:
|
|
|
|
TestedDomain - Domain the DC is in
|
|
|
|
Return Value:
|
|
|
|
Returns pointer to structure describing the DC
|
|
|
|
NULL: There are no 'up' DCs
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PTESTED_DC TestedDc;
|
|
|
|
|
|
//
|
|
// Find a DC that's up to run the test
|
|
//
|
|
|
|
for ( ListEntry = TestedDomain->TestedDcs.Flink ;
|
|
ListEntry != &TestedDomain->TestedDcs ;
|
|
ListEntry = ListEntry->Flink ) {
|
|
|
|
|
|
//
|
|
// Loop through the list of DCs in this domain
|
|
//
|
|
|
|
TestedDc = CONTAINING_RECORD( ListEntry, TESTED_DC, Next );
|
|
|
|
if ( (TestedDc->Flags & DC_IS_DOWN) == 0) {
|
|
return TestedDc;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
PTESTED_DC
|
|
AddTestedDc(
|
|
IN NETDIAG_PARAMS *pParams,
|
|
IN OUT NETDIAG_RESULT *pResults,
|
|
IN PTESTED_DOMAIN TestedDomain,
|
|
IN LPWSTR ComputerName,
|
|
IN ULONG Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add a DC to the list of DCs to test in a particular domain
|
|
|
|
Arguments:
|
|
|
|
TestedDomain - Domain the DC is in
|
|
|
|
ComputerName - Netbios or DNS computer name of the DC
|
|
Without the leading \\
|
|
|
|
Flags - Flags to set on the DC
|
|
|
|
Return Value:
|
|
|
|
Returns pointer to structure describing the DC
|
|
|
|
NULL: Memory allocation failure.
|
|
|
|
--*/
|
|
{
|
|
PTESTED_DC TestedDc = NULL;
|
|
PLIST_ENTRY ListEntry;
|
|
LPWSTR Period;
|
|
|
|
|
|
|
|
//
|
|
// Check if the domain is already defined.
|
|
//
|
|
|
|
TestedDc = FindTestedDc( pResults, ComputerName );
|
|
|
|
//
|
|
// Ensure the DC is for the right domain
|
|
//
|
|
|
|
if ( TestedDc != NULL )
|
|
{
|
|
if ( TestedDc->TestedDomain != TestedDomain )
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate a structure to describe the domain.
|
|
//
|
|
|
|
if ( TestedDc == NULL )
|
|
{
|
|
TestedDc = Malloc( sizeof(TESTED_DC) );
|
|
|
|
if ( TestedDc == NULL )
|
|
{
|
|
DebugMessage(" AddTestedDc(): Out of Memory!\n");
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory( TestedDc, sizeof(TESTED_DC) );
|
|
|
|
TestedDc->ComputerName = NetpAllocWStrFromWStr( ComputerName );
|
|
|
|
if ( TestedDc->ComputerName == NULL )
|
|
{
|
|
Free(TestedDc);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Convert the computername to netbios (Use the API when in becomes available.
|
|
//
|
|
|
|
if ((Period = wcschr( ComputerName, L'.' )) == NULL )
|
|
{
|
|
wcsncpy( TestedDc->NetbiosDcName, ComputerName, CNLEN );
|
|
TestedDc->NetbiosDcName[CNLEN] = L'\0';
|
|
}
|
|
else
|
|
{
|
|
ULONG CharsToCopy = (ULONG) min( CNLEN, Period-ComputerName);
|
|
|
|
wcsncpy( TestedDc->NetbiosDcName, ComputerName, CharsToCopy );
|
|
TestedDc->NetbiosDcName[CharsToCopy] = '\0';
|
|
}
|
|
|
|
TestedDc->TestedDomain = TestedDomain;
|
|
|
|
InsertTailList( &TestedDomain->TestedDcs, &TestedDc->Next );
|
|
}
|
|
|
|
//
|
|
// Set the flags requested by the caller.
|
|
//
|
|
|
|
// if ( Flags & DC_IS_NT5 ) {
|
|
// if ( TestedDc->Flags & DC_IS_NT4 ) {
|
|
// printf(" [WARNING] '%ws' is both an NT 5 and NT 4 DC.\n", ComputerName );
|
|
// }
|
|
// }
|
|
// if ( Flags & DC_IS_NT4 ) {
|
|
// if ( TestedDc->Flags & DC_IS_NT5 ) {
|
|
// printf(" [WARNING] '%ws' is both an NT 4 and NT 5 DC.\n", ComputerName );
|
|
// }
|
|
// }
|
|
TestedDc->Flags |= Flags;
|
|
|
|
//
|
|
// Ensure we have the IpAddress of this DC.
|
|
//
|
|
|
|
(VOID) GetIpAddressForDc( TestedDc );
|
|
|
|
|
|
//
|
|
// Ping the DC
|
|
//
|
|
|
|
if ( (TestedDc->Flags & DC_PINGED) == 0 && (TestedDc->Flags & DC_IS_DOWN) == 0)
|
|
{
|
|
if ( !IsIcmpResponseW( TestedDc->DcIpAddress ) ) {
|
|
DebugMessage2(" [WARNING] Cannot ping '%ws' (it must be down).\n", TestedDc->ComputerName );
|
|
TestedDc->Flags |= DC_IS_DOWN;
|
|
TestedDc->Flags |= DC_FAILED_PING;
|
|
}
|
|
TestedDc->Flags |= DC_PINGED;
|
|
}
|
|
|
|
//try to query DC info to check if the DC is really up
|
|
if( (TestedDc->Flags & DC_IS_DOWN) == 0 )
|
|
{
|
|
PSERVER_INFO_100 pServerInfo100 = NULL;
|
|
NET_API_STATUS NetStatus;
|
|
NetStatus = NetServerGetInfo( TestedDc->ComputerName,
|
|
100,
|
|
(LPBYTE *)&pServerInfo100 );
|
|
if(NetStatus != NO_ERROR && NetStatus != ERROR_ACCESS_DENIED)
|
|
{
|
|
TestedDc->Flags |= DC_IS_DOWN;
|
|
|
|
// IDS_GLOBAL_DC_DOWN "Cannot get information for DC %ws. [%s] Assume it is down.\n"
|
|
PrintDebug(pParams, 4, IDS_GLOBAL_DC_DOWN, TestedDc->ComputerName,
|
|
NetStatusToString(NetStatus));
|
|
}
|
|
else
|
|
NetApiBufferFree( pServerInfo100 );
|
|
|
|
}
|
|
|
|
return TestedDc;
|
|
}
|
|
|
|
|
|
|
|
PTESTED_DC
|
|
FindTestedDc(
|
|
IN OUT NETDIAG_RESULT *pResults,
|
|
IN LPWSTR ComputerName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find the tested DC structure for the named DC
|
|
|
|
Arguments:
|
|
|
|
ComputerName - Netbios or DNS computer name of the DC
|
|
Without the leading \\
|
|
|
|
Return Value:
|
|
|
|
Returns pointer to structure describing the DC
|
|
|
|
NULL: No Such DC is currently defined
|
|
|
|
--*/
|
|
{
|
|
PTESTED_DC TestedDc = NULL;
|
|
PTESTED_DOMAIN TestedDomain = NULL;
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ENTRY ListEntry2;
|
|
WCHAR NetbiosDcName[CNLEN+1];
|
|
LPWSTR Period;
|
|
|
|
//
|
|
// Convert the computername to netbios (Use the API when in becomes available.
|
|
//
|
|
|
|
if ((Period = wcschr( ComputerName, L'.' )) == NULL )
|
|
{
|
|
wcsncpy( NetbiosDcName, ComputerName, CNLEN );
|
|
NetbiosDcName[CNLEN] = L'\0';
|
|
}
|
|
else
|
|
{
|
|
ULONG CharsToCopy = (ULONG) min( CNLEN, Period-ComputerName);
|
|
|
|
wcsncpy( NetbiosDcName, ComputerName, CharsToCopy );
|
|
NetbiosDcName[CharsToCopy] = '\0';
|
|
}
|
|
|
|
|
|
//
|
|
// Loop through the list of domains
|
|
//
|
|
|
|
for ( ListEntry = pResults->Global.listTestedDomains.Flink ;
|
|
ListEntry != &pResults->Global.listTestedDomains ;
|
|
ListEntry = ListEntry->Flink ) {
|
|
|
|
|
|
//
|
|
// Loop through the list of DCs in this domain
|
|
//
|
|
|
|
TestedDomain = CONTAINING_RECORD( ListEntry, TESTED_DOMAIN, Next );
|
|
|
|
for ( ListEntry2 = TestedDomain->TestedDcs.Flink ;
|
|
ListEntry2 != &TestedDomain->TestedDcs ;
|
|
ListEntry2 = ListEntry2->Flink ) {
|
|
|
|
|
|
//
|
|
// Loop through the list of DCs in this domain
|
|
//
|
|
|
|
TestedDc = CONTAINING_RECORD( ListEntry2, TESTED_DC, Next );
|
|
|
|
|
|
//
|
|
// If the Netbios computer names match,
|
|
// we found it.
|
|
//
|
|
|
|
if ( _wcsicmp( TestedDc->NetbiosDcName, NetbiosDcName ) == 0 ) {
|
|
return TestedDc;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
GetADc(IN NETDIAG_PARAMS *pParams,
|
|
IN OUT NETDIAG_RESULT *pResults,
|
|
OUT PLIST_ENTRY plmsgOutput,
|
|
IN DSGETDCNAMEW *DsGetDcRoutine,
|
|
IN PTESTED_DOMAIN TestedDomain,
|
|
IN DWORD Flags,
|
|
OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does a DsGetDcName
|
|
|
|
Arguments:
|
|
|
|
DsGetDcRoutine - Routine to call to find a DC
|
|
|
|
TestedDomain - Domain to test
|
|
|
|
Flags - Flags to pass to DsGetDcName
|
|
|
|
DomainControllerInfo - Return Domain Controller information
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
PDOMAIN_CONTROLLER_INFOW LocalDomainControllerInfo = NULL;
|
|
PDOMAIN_CONTROLLER_INFOW LocalDomainControllerInfo2;
|
|
static BOOL s_fDcNameInitialized = FALSE;
|
|
|
|
//
|
|
// Initialize internal version of DsGetDcName
|
|
//
|
|
if( !s_fDcNameInitialized )
|
|
{
|
|
|
|
// Commented out to port to Source Depot - smanda
|
|
#ifdef SLM_TREE
|
|
NetStatus = DCNameInitialize();
|
|
|
|
if ( NetStatus != NO_ERROR )
|
|
{
|
|
DebugMessage2(" [FATAL] Cannot initialize DsGetDcName. [%s]\n",
|
|
NetStatusToString(NetStatus));
|
|
PrintGuru( NetStatus, DSGETDC_GURU );
|
|
goto Cleanup;
|
|
}
|
|
#endif
|
|
|
|
//$REVIEW (nsun 10/05/98) make sure we just init once
|
|
s_fDcNameInitialized = TRUE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Try it first not asking for IP.
|
|
//
|
|
// Though technically wrong, specify DS_DIRECTORY_SERVICE_PREFERRED here
|
|
// or I won't be able to tell that this is an NT 5 domain below.
|
|
//
|
|
|
|
NetStatus = (*DsGetDcRoutine)( NULL,
|
|
TestedDomain->QueryableDomainName,
|
|
NULL,
|
|
NULL,
|
|
DS_FORCE_REDISCOVERY |
|
|
DS_DIRECTORY_SERVICE_PREFERRED |
|
|
Flags,
|
|
&LocalDomainControllerInfo );
|
|
|
|
// If DsGetDcName return ERROR_NO_SUCH_DOMAIN then try to findout the exact reason for the error
|
|
// Based on DoctorDNS specs for join command
|
|
if ( NetStatus == ERROR_NO_SUCH_DOMAIN && TestedDomain->QueryableDomainName != NULL && plmsgOutput != NULL ) {
|
|
CheckDomainConfig(TestedDomain->QueryableDomainName, plmsgOutput);
|
|
}
|
|
|
|
if ( NetStatus != NO_ERROR ) {
|
|
DebugMessage2( " DsGetDcRoutine failed. [%s]\n", NetStatusToString(NetStatus));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Add this DC to the list of DCs in the domain
|
|
//
|
|
|
|
(VOID) AddTestedDc( pParams,
|
|
pResults,
|
|
TestedDomain,
|
|
LocalDomainControllerInfo->DomainControllerName+2,
|
|
(LocalDomainControllerInfo->Flags & DS_DS_FLAG ) ?
|
|
DC_IS_NT5 :
|
|
DC_IS_NT4 );
|
|
|
|
//
|
|
// If this DC wasn't discovered using IP,
|
|
// and it is an NT 5 DC,
|
|
// try again requiring IP.
|
|
//
|
|
// (I can't require IP in the first place since NT 4.0 DCs can't return
|
|
// their IP address.)
|
|
//
|
|
|
|
if ( LocalDomainControllerInfo->DomainControllerAddressType != DS_INET_ADDRESS &&
|
|
(LocalDomainControllerInfo->Flags & DS_DS_FLAG) != 0 ) {
|
|
|
|
NetStatus = (*DsGetDcRoutine)( NULL,
|
|
TestedDomain->QueryableDomainName,
|
|
NULL,
|
|
NULL,
|
|
DS_FORCE_REDISCOVERY |
|
|
DS_IP_REQUIRED |
|
|
Flags,
|
|
&LocalDomainControllerInfo2 );
|
|
|
|
if ( NetStatus == NO_ERROR ) {
|
|
NetApiBufferFree( LocalDomainControllerInfo );
|
|
LocalDomainControllerInfo = LocalDomainControllerInfo2;
|
|
|
|
//
|
|
// Add this DC to the list of DCs in the domain
|
|
//
|
|
|
|
(VOID) AddTestedDc( pParams,
|
|
pResults,
|
|
TestedDomain,
|
|
LocalDomainControllerInfo->DomainControllerName+2,
|
|
(LocalDomainControllerInfo->Flags & DS_DS_FLAG ) ?
|
|
DC_IS_NT5 :
|
|
DC_IS_NT4 );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Check to ensure KDC consistency
|
|
//
|
|
|
|
// This is also checked in DoDsGetDcName()
|
|
if ( (LocalDomainControllerInfo->Flags & (DS_DS_FLAG|DS_KDC_FLAG)) == DS_DS_FLAG ) {
|
|
DebugMessage3(" [WARNING] KDC is not running on NT 5 DC '%ws' in domain '%ws'.",
|
|
LocalDomainControllerInfo->DomainControllerName,
|
|
TestedDomain->PrintableDomainName );
|
|
}
|
|
|
|
//
|
|
// Return the info to the caller
|
|
//
|
|
|
|
*DomainControllerInfo = LocalDomainControllerInfo;
|
|
LocalDomainControllerInfo = NULL;
|
|
NetStatus = NO_ERROR;
|
|
|
|
Cleanup:
|
|
if ( LocalDomainControllerInfo != NULL ) {
|
|
NetApiBufferFree( LocalDomainControllerInfo );
|
|
LocalDomainControllerInfo = NULL;
|
|
}
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
|
|
//used in DCList and LDAP tests
|
|
BOOL
|
|
GetIpAddressForDc( PTESTED_DC TestedDc )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the IP address for the tested DC
|
|
|
|
Arguments:
|
|
|
|
TestedDc - DC to get the IP address for.
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE: Test suceeded.
|
|
FALSE: Test failed
|
|
|
|
--*/
|
|
{
|
|
BOOL RetVal = TRUE;
|
|
NET_API_STATUS NetStatus;
|
|
HOSTENT *HostEnt;
|
|
LPSTR AnsiComputerName;
|
|
|
|
if ( TestedDc->DcIpAddress == NULL ) {
|
|
|
|
AnsiComputerName = NetpAllocStrFromWStr( TestedDc->ComputerName );
|
|
|
|
if ( AnsiComputerName == NULL ) {
|
|
DebugMessage( "Out of memory!\n" );
|
|
RetVal = FALSE;
|
|
TestedDc->Flags |= DC_IS_DOWN;
|
|
} else {
|
|
|
|
HostEnt = gethostbyname( AnsiComputerName );
|
|
|
|
NetApiBufferFree( AnsiComputerName );
|
|
|
|
if ( HostEnt == NULL )
|
|
{
|
|
NetStatus = WSAGetLastError();
|
|
DebugMessage3(" [WARNING] Cannot gethostbyname for '%ws'. [%s]\n",
|
|
TestedDc->ComputerName, NetStatusToString(NetStatus) );
|
|
TestedDc->Flags |= DC_IS_DOWN;
|
|
}
|
|
else
|
|
{
|
|
WCHAR LocalIpAddressString[NL_IP_ADDRESS_LENGTH+1];
|
|
NetpIpAddressToWStr( *(PULONG)HostEnt->h_addr_list[0], LocalIpAddressString );
|
|
TestedDc->DcIpAddress = NetpAllocWStrFromWStr( LocalIpAddressString );
|
|
if (TestedDc->DcIpAddress == NULL )
|
|
{
|
|
RetVal = FALSE;
|
|
TestedDc->Flags |= DC_IS_DOWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
|
|
DWORD CheckDomainConfig(IN PWSTR pwzDomainName, OUT PLIST_ENTRY plmsgOutput)
|
|
{
|
|
DNS_STATUS status;
|
|
status = ValidateDnsDomainName(pwzDomainName, plmsgOutput);
|
|
if (status == ERROR_SUCCESS)
|
|
{
|
|
status = CheckAdapterDnsConfig(plmsgOutput);
|
|
if (status == ERROR_SUCCESS)
|
|
{
|
|
status = DnsDcSrvCheck(pwzDomainName, plmsgOutput);
|
|
}
|
|
else
|
|
{
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13242);
|
|
}
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: CheckAdapterDnsConfig
|
|
//
|
|
// Synopsis: Check whether at least one enabled adapter/connection is
|
|
// configured with a DNS server.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DWORD
|
|
CheckAdapterDnsConfig( OUT PLIST_ENTRY plmsgOutput )
|
|
{
|
|
// IpConfig reads the registry and I can't find a good alternative way to do
|
|
// this remotely. For now using DnsQueryConfig which is not remoteable nor
|
|
// does it return per-adapter listings.
|
|
//
|
|
PIP_ARRAY pipArray;
|
|
DNS_STATUS status;
|
|
DWORD i, dwBufSize = sizeof(IP_ARRAY);
|
|
|
|
status = DnsQueryConfig(DnsConfigDnsServerList, DNS_CONFIG_FLAG_ALLOC, NULL,
|
|
NULL, &pipArray, &dwBufSize);
|
|
|
|
if (ERROR_SUCCESS != status || !pipArray)
|
|
{
|
|
DebugMessage2(L"Attempt to obtain DNS name server info failed with error %d\n", status);
|
|
return status;
|
|
}
|
|
|
|
return (pipArray->AddrCount) ? ERROR_SUCCESS : DNS_INFO_NO_RECORDS;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DnsDcSrvCheck
|
|
//
|
|
// Synopsis: Check whether the SRV DNS record for
|
|
// _ldap._tcp.dc._msdcs.<DNS name of Active Directory Domain>
|
|
// is in place.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DWORD
|
|
DnsDcSrvCheck(PWSTR pwzDnsDomain, OUT PLIST_ENTRY plmsgOutput)
|
|
{
|
|
PDNS_RECORD rgDnsRecs, pDnsRec;
|
|
DNS_STATUS status;
|
|
BOOL fSuccess;
|
|
PWSTR pwzFullSrvRecord, pwzSrvList = NULL;
|
|
|
|
pwzFullSrvRecord = ConcatonateStrings(g_pwzSrvRecordPrefix, pwzDnsDomain);
|
|
|
|
if (!pwzFullSrvRecord)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
// First query for the SRV records for this
|
|
status = DnsQuery_W(pwzFullSrvRecord, DNS_TYPE_SRV, DNS_QUERY_BYPASS_CACHE,
|
|
NULL, &rgDnsRecs, NULL);
|
|
|
|
pDnsRec = rgDnsRecs;
|
|
|
|
if (ERROR_SUCCESS == status)
|
|
{
|
|
if (!pDnsRec)
|
|
{
|
|
// PrintMsg(SEV_ALWAYS, DCDIAG_REPLICA_ERR_NO_SRV, pwzDnsDomain);
|
|
}
|
|
else
|
|
{
|
|
PDNS_RECORD rgARecs;
|
|
fSuccess = FALSE;
|
|
|
|
while (pDnsRec)
|
|
{
|
|
if (DNS_TYPE_SRV == pDnsRec->wType)
|
|
{
|
|
WCHAR UnicodeDCName[MAX_PATH+1];
|
|
NetpCopyStrToWStr( UnicodeDCName, pDnsRec->Data.Srv.pNameTarget);
|
|
status = DnsQuery_W(UnicodeDCName, DNS_TYPE_A,
|
|
DNS_QUERY_BYPASS_CACHE,
|
|
NULL, &rgARecs, NULL);
|
|
|
|
if (ERROR_SUCCESS != status || !rgARecs)
|
|
{
|
|
// failure.
|
|
if (!AddToList(&pwzSrvList, UnicodeDCName))
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fSuccess = TRUE;
|
|
DebugMessage2(L"SRV name: %s\n",
|
|
pDnsRec->Data.Srv.nameTarget);
|
|
DnsRecordListFree(rgARecs, TRUE);
|
|
}
|
|
}
|
|
pDnsRec = pDnsRec->pNext;
|
|
}
|
|
|
|
DnsRecordListFree(rgDnsRecs, TRUE);
|
|
|
|
if (fSuccess)
|
|
{
|
|
// Success message
|
|
}
|
|
else
|
|
{
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13243,
|
|
pwzDnsDomain, pwzSrvList);
|
|
LocalFree(pwzSrvList);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PWSTR pwzDomainList;
|
|
|
|
switch (status)
|
|
{
|
|
case DNS_ERROR_RCODE_FORMAT_ERROR:
|
|
case DNS_ERROR_RCODE_NOT_IMPLEMENTED:
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13244,
|
|
pwzDnsDomain );
|
|
break;
|
|
|
|
case DNS_ERROR_RCODE_SERVER_FAILURE:
|
|
if (!BuildDomainList(&pwzDomainList, pwzDnsDomain))
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13245,
|
|
pwzDnsDomain, pwzFullSrvRecord, pwzDomainList );
|
|
LocalFree(pwzDomainList);
|
|
break;
|
|
|
|
case DNS_ERROR_RCODE_NAME_ERROR:
|
|
if (!BuildDomainList(&pwzDomainList, pwzDnsDomain))
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13246,
|
|
pwzDnsDomain, pwzDnsDomain, pwzFullSrvRecord, pwzDomainList );
|
|
LocalFree(pwzDomainList);
|
|
break;
|
|
|
|
case DNS_ERROR_RCODE_REFUSED:
|
|
if (!BuildDomainList(&pwzDomainList, pwzDnsDomain))
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13247,
|
|
pwzDnsDomain, pwzDomainList );
|
|
LocalFree(pwzDomainList);
|
|
break;
|
|
|
|
case DNS_INFO_NO_RECORDS:
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13248,
|
|
pwzDnsDomain, pwzDnsDomain, pwzDnsDomain );
|
|
break;
|
|
|
|
case ERROR_TIMEOUT:
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13249);
|
|
break;
|
|
|
|
default:
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13250,
|
|
status );
|
|
break;
|
|
}
|
|
}
|
|
|
|
LocalFree(pwzFullSrvRecord);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: ValidateDnsDomainName
|
|
//
|
|
// Synopsis: Validate the DNS domain name.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DWORD
|
|
ValidateDnsDomainName(PWSTR pwzDnsDomain, OUT PLIST_ENTRY plmsgOutput)
|
|
{
|
|
DNS_STATUS status;
|
|
|
|
status = DnsValidateName_W(pwzDnsDomain, DnsNameDomain);
|
|
|
|
switch (status)
|
|
{
|
|
case ERROR_INVALID_NAME:
|
|
case DNS_ERROR_INVALID_NAME_CHAR:
|
|
case DNS_ERROR_NUMERIC_NAME:
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13240,
|
|
pwzDnsDomain, DNS_MAX_LABEL_LENGTH );
|
|
return status;
|
|
|
|
case DNS_ERROR_NON_RFC_NAME:
|
|
//
|
|
// Not an error, print warning message.
|
|
//
|
|
AddMessageToList(plmsgOutput, Nd_Quiet, IDS_DSGETDC_13241,
|
|
pwzDnsDomain );
|
|
break;
|
|
|
|
case ERROR_SUCCESS:
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
PWSTR ConcatonateStrings(PWSTR pwzFirst, PWSTR pwzSecond)
|
|
{
|
|
PWSTR pwz;
|
|
|
|
pwz = (PWSTR)LocalAlloc(LMEM_FIXED,
|
|
((int)wcslen(pwzFirst) + (int)wcslen(pwzSecond) + 1) * sizeof(WCHAR));
|
|
|
|
if (!pwz)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
wcscpy(pwz, pwzFirst);
|
|
wcscat(pwz, pwzSecond);
|
|
|
|
return pwz;
|
|
}
|
|
|
|
BOOL AddToList(PWSTR * ppwzList, PWSTR pwz)
|
|
{
|
|
PWSTR pwzTmp;
|
|
|
|
if (*ppwzList)
|
|
{
|
|
pwzTmp = (PWSTR)LocalAlloc(LMEM_FIXED,
|
|
((int)wcslen(*ppwzList) + (int)wcslen(pwz) + 3) * sizeof(WCHAR));
|
|
if (!pwzTmp)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
wcscpy(pwzTmp, *ppwzList);
|
|
wcscat(pwzTmp, L", ");
|
|
wcscat(pwzTmp, pwz);
|
|
|
|
LocalFree(*ppwzList);
|
|
|
|
*ppwzList = pwzTmp;
|
|
}
|
|
else
|
|
{
|
|
pwzTmp = AllocString(pwz);
|
|
|
|
if (!pwzTmp)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*ppwzList = pwzTmp;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL BuildDomainList(PWSTR * ppwzDomainList, PWSTR pwzDnsDomain)
|
|
{
|
|
PWSTR pwzDot, pwzTmp;
|
|
|
|
pwzTmp = AllocString(pwzDnsDomain);
|
|
|
|
if (!pwzTmp)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
pwzDot = pwzDnsDomain;
|
|
|
|
while (pwzDot = wcschr(pwzDot, L'.'))
|
|
{
|
|
pwzDot++;
|
|
if (!pwzDot)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!AddToList(&pwzTmp, pwzDot))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*ppwzDomainList = pwzTmp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// string helpers.
|
|
|
|
PWSTR AllocString(PWSTR pwz)
|
|
{
|
|
PWSTR pwzTmp;
|
|
|
|
pwzTmp = (PWSTR)LocalAlloc(LMEM_FIXED, ((int)wcslen(pwz) + 1) * sizeof(WCHAR));
|
|
|
|
if (!pwzTmp)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
wcscpy(pwzTmp, pwz);
|
|
|
|
return pwzTmp;
|
|
}
|
|
|
|
|
|
VOID
|
|
NetpIpAddressToStr(
|
|
ULONG IpAddress,
|
|
CHAR IpAddressString[NL_IP_ADDRESS_LENGTH+1]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert an IP address to a string.
|
|
|
|
Arguments:
|
|
|
|
IpAddress - IP Address to convert
|
|
|
|
IpAddressString - resultant string.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
struct in_addr InetAddr;
|
|
char * InetAddrString;
|
|
|
|
//
|
|
// Convert the address to ascii
|
|
//
|
|
InetAddr.s_addr = IpAddress;
|
|
InetAddrString = inet_ntoa( InetAddr );
|
|
|
|
//
|
|
// Copy the string our to the caller.
|
|
//
|
|
|
|
if ( InetAddrString == NULL || strlen(InetAddrString) > NL_IP_ADDRESS_LENGTH ) {
|
|
*IpAddressString = L'\0';
|
|
} else {
|
|
strcpy( IpAddressString, InetAddrString );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
NetpIpAddressToWStr(
|
|
ULONG IpAddress,
|
|
WCHAR IpAddressString[NL_IP_ADDRESS_LENGTH+1]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert an IP address to a string.
|
|
|
|
Arguments:
|
|
|
|
IpAddress - IP Address to convert
|
|
|
|
IpAddressString - resultant string.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
CHAR IpAddressStr[NL_IP_ADDRESS_LENGTH+1];
|
|
NetpIpAddressToStr( IpAddress, IpAddressStr );
|
|
NetpCopyStrToWStr( IpAddressString, IpAddressStr );
|
|
}
|
|
|
|
|
|
NET_API_STATUS
|
|
NetpDcBuildPing(
|
|
IN BOOL PdcOnly,
|
|
IN ULONG RequestCount,
|
|
IN LPCWSTR UnicodeComputerName,
|
|
IN LPCWSTR UnicodeUserName OPTIONAL,
|
|
IN LPCSTR ResponseMailslotName,
|
|
IN ULONG AllowableAccountControlBits,
|
|
IN PSID RequestedDomainSid OPTIONAL,
|
|
IN ULONG NtVersion,
|
|
OUT PVOID *Message,
|
|
OUT PULONG MessageSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Build the message to ping a DC to see if it exists.
|
|
Copied from net\svcdlls\logonsv\netpdc.c
|
|
|
|
Arguments:
|
|
|
|
PdcOnly - True if only the PDC should respond.
|
|
|
|
RequestCount - Retry count of this operation.
|
|
|
|
UnicodeComputerName - Netbios computer name of the machine to respond to.
|
|
|
|
UnicodeUserName - Account name of the user being pinged.
|
|
If NULL, DC will always respond affirmatively.
|
|
|
|
ResponseMailslotName - Name of the mailslot DC is to respond to.
|
|
|
|
AllowableAccountControlBits - Mask of allowable account types for UnicodeUserName.
|
|
|
|
RequestedDomainSid - Sid of the domain the message is destined to.
|
|
|
|
NtVersion - Version of the message.
|
|
0: For backward compatibility.
|
|
NETLOGON_NT_VERSION_5: for NT 5.0 message.
|
|
NETLOGON_NT_VERSION_5EX: for extended NT 5.0 message
|
|
|
|
Message - Returns the message to be sent to the DC in question.
|
|
Buffer must be free using NetpMemoryFree().
|
|
|
|
MessageSize - Returns the size (in bytes) of the returned message
|
|
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - Operation completed successfully;
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY - The message could not be allocated.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
LPSTR Where;
|
|
PNETLOGON_SAM_LOGON_REQUEST SamLogonRequest = NULL;
|
|
LPSTR OemComputerName = NULL;
|
|
|
|
//
|
|
// If only the PDC should respond,
|
|
// build a primary query packet.
|
|
//
|
|
|
|
if ( PdcOnly ) {
|
|
PNETLOGON_LOGON_QUERY LogonQuery;
|
|
|
|
//
|
|
// Allocate memory for the primary query message.
|
|
//
|
|
|
|
SamLogonRequest = NetpMemoryAllocate( sizeof(NETLOGON_LOGON_QUERY) );
|
|
|
|
if( SamLogonRequest == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
LogonQuery = (PNETLOGON_LOGON_QUERY)SamLogonRequest;
|
|
|
|
|
|
|
|
//
|
|
// Translate to get an Oem computer name.
|
|
//
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
OemComputerName = NetpLogonUnicodeToOem( (LPWSTR)UnicodeComputerName );
|
|
#else
|
|
OemComputerName = MyNetpLogonUnicodeToOem( (LPWSTR)UnicodeComputerName );
|
|
#endif
|
|
|
|
if ( OemComputerName == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Build the query message.
|
|
//
|
|
|
|
LogonQuery->Opcode = LOGON_PRIMARY_QUERY;
|
|
|
|
Where = LogonQuery->ComputerName;
|
|
|
|
NetpLogonPutOemString(
|
|
OemComputerName,
|
|
sizeof(LogonQuery->ComputerName),
|
|
&Where );
|
|
|
|
NetpLogonPutOemString(
|
|
(LPSTR) ResponseMailslotName,
|
|
sizeof(LogonQuery->MailslotName),
|
|
&Where );
|
|
|
|
NetpLogonPutUnicodeString(
|
|
(LPWSTR) UnicodeComputerName,
|
|
sizeof( LogonQuery->UnicodeComputerName ),
|
|
&Where );
|
|
|
|
// Join common code to add NT 5 specific data.
|
|
|
|
|
|
//
|
|
// If any DC can respond,
|
|
// build a logon query packet.
|
|
//
|
|
|
|
} else {
|
|
ULONG DomainSidSize;
|
|
|
|
//
|
|
// Allocate memory for the logon request message.
|
|
//
|
|
|
|
#ifndef WIN32_CHICAGO
|
|
if ( RequestedDomainSid != NULL ) {
|
|
DomainSidSize = RtlLengthSid( RequestedDomainSid );
|
|
} else {
|
|
DomainSidSize = 0;
|
|
}
|
|
#else // WIN32_CHICAGO
|
|
DomainSidSize = 0;
|
|
#endif // WIN32_CHICAGO
|
|
|
|
SamLogonRequest = NetpMemoryAllocate(
|
|
sizeof(NETLOGON_SAM_LOGON_REQUEST) +
|
|
DomainSidSize +
|
|
sizeof(DWORD) // for SID alignment on 4 byte boundary
|
|
);
|
|
|
|
if( SamLogonRequest == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Build the query message.
|
|
//
|
|
|
|
SamLogonRequest->Opcode = LOGON_SAM_LOGON_REQUEST;
|
|
SamLogonRequest->RequestCount = (WORD) RequestCount;
|
|
|
|
Where = (PCHAR) &SamLogonRequest->UnicodeComputerName;
|
|
|
|
NetpLogonPutUnicodeString(
|
|
(LPWSTR) UnicodeComputerName,
|
|
sizeof(SamLogonRequest->UnicodeComputerName),
|
|
&Where );
|
|
|
|
NetpLogonPutUnicodeString(
|
|
(LPWSTR) UnicodeUserName,
|
|
sizeof(SamLogonRequest->UnicodeUserName),
|
|
&Where );
|
|
|
|
NetpLogonPutOemString(
|
|
(LPSTR) ResponseMailslotName,
|
|
sizeof(SamLogonRequest->MailslotName),
|
|
&Where );
|
|
|
|
NetpLogonPutBytes(
|
|
&AllowableAccountControlBits,
|
|
sizeof(SamLogonRequest->AllowableAccountControlBits),
|
|
&Where );
|
|
|
|
//
|
|
// Place domain SID in the message.
|
|
//
|
|
|
|
NetpLogonPutBytes( &DomainSidSize, sizeof(DomainSidSize), &Where );
|
|
NetpLogonPutDomainSID( RequestedDomainSid, DomainSidSize, &Where );
|
|
|
|
}
|
|
|
|
NetpLogonPutNtToken( &Where, NtVersion );
|
|
|
|
//
|
|
// Return the message to the caller.
|
|
//
|
|
|
|
*Message = SamLogonRequest;
|
|
*MessageSize = (ULONG)(Where - (PCHAR)SamLogonRequest);
|
|
SamLogonRequest = NULL;
|
|
|
|
NetStatus = NO_ERROR;
|
|
|
|
|
|
//
|
|
// Free locally used resources.
|
|
//
|
|
Cleanup:
|
|
|
|
if ( OemComputerName != NULL ) {
|
|
NetpMemoryFree( OemComputerName );
|
|
}
|
|
|
|
if ( SamLogonRequest != NULL ) {
|
|
NetpMemoryFree( SamLogonRequest );
|
|
}
|
|
return NetStatus;
|
|
}
|
|
|