Source code of Windows XP (NT5)
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
30 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;
}