|
|
//++
//
// 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; }
|