windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/logonsrv/client/getdcnam.c
2020-09-26 16:20:57 +08:00

4229 lines
111 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1987-1991 Microsoft Corporation
Module Name:
getdcnam.c
Abstract:
NetGetDCName API
Author:
Ported from Lan Man 2.0
Environment:
User mode only.
Contains NT-specific code.
Requires ANSI C extensions: slash-slash comments, long external names.
Revision History:
09-Feb-1989 (PaulC)
Created file, to hold NetGetDCName.
18-Apr-1989 (Ericpe)
Implemented NetGetDCName.
30-May-1989 (DannyGl)
Reduced DosReadMailslot timeout.
07-Jul-1989 (NealF)
Use I_NetNameCanonicalize
27-Jul-1989 (WilliamW)
Use WIN3 manifest for WIN3.0 compatibility
03-Jan-1990 (WilliamW)
canonicalize domain and use I_NetCompareName
08-Jun-1991 (CliffV)
Ported to NT
23-Jul-1991 JohnRo
Implement downlevel NetGetDCName.
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#ifndef WIN32_CHICAGO
#include <rpc.h>
#include <ntrpcp.h> // needed by rpcasync.h
#include <rpcasync.h> // I_RpcExceptionFilter
#include <logon_c.h>// includes lmcons.h, lmaccess.h, netlogon.h, ssi.h, windef.h
#else // WIN32_CHICAGO
#include <windef.h>
#include <lmcons.h>
#endif // WIN32_CHICAGO
#include <stdio.h>
#include <winbase.h>
#include <winsock2.h>
#ifndef WIN32_CHICAGO
#include <accessp.h>
#include <align.h>
#endif // WIN32_CHICAGO
#include <debuglib.h> // IF_DEBUG()
#include <dsgetdc.h> // DsGetDcName()
#include <dsgetdcp.h> // DsGetDcNameWithAccount()
#include <icanon.h> // NAMETYPE_* defines NetpIsRemote(), NIRFLAG_ equates.
#include <lmapibuf.h>
#include <lmerr.h>
#ifndef WIN32_CHICAGO
#include <lmremutl.h> // SUPPORTS_* defines
#include <lmserver.h> // SV_TYPE_* defines
#include <lmsvc.h> // SERVICE_* defines
#include <lmwksta.h>
#include <logonp.h> // NetpLogon routines
#include <nlbind.h> // Netlogon RPC binding cache init routines
#endif // WIN32_CHICAGO
#include <netdebug.h> // NetpKdPrint
#include <netlib.h> // NetpMemoryFree
#ifndef WIN32_CHICAGO
#include <netlibnt.h> // NetpApiStatusToNtStatus
#include <netrpc.h>
#include <rxdomain.h> // RxNetGetDCName().
#include <string.h>
#include <stdlib.h>
#endif // WIN32_CHICAGO
#include <tstring.h> // NetpCopyStrToWStr()
#if DBG
#define NETLOGONDBG 1
#endif // DBG
#include <nldebug.h> // NlPrint()
#include <ntddbrow.h> // Needed by nlcommon.h
#include <nlcommon.h> // Definitions shared with netlogon
//
// Only dynamically initialize winsock.
//
CRITICAL_SECTION GlobalDCNameCritSect;
BOOLEAN DsGetDcWsaInitialized;
#define LOCKDOMAINSEM() EnterCriticalSection( &GlobalDCNameCritSect )
#define UNLOCKDOMAINSEM() LeaveCriticalSection( &GlobalDCNameCritSect )
// end global dll data
#ifdef WIN32_CHICAGO // from net\inc\logonp.h
NET_API_STATUS
NetpLogonWriteMailslot(
IN LPWSTR MailslotName,
IN LPVOID Buffer,
IN DWORD BufferSize
);
NTSTATUS
NetpApiStatusToNtStatus(
NET_API_STATUS NetStatus
);
#endif // WIN32_CHICAGO
NET_API_STATUS
DCNameInitialize(
VOID
)
/*++
Routine Description:
Perform per-process initialization.
Arguments:
None.
Return Value:
Status of the operation.
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
#ifndef NETTEST_UTILITY
#ifndef WIN32_CHICAGO
//
// Initialize the RPC binding cache.
//
NetStatus = NlBindingAttachDll();
if ( NetStatus != NO_ERROR ) {
return NetStatus;
}
#endif // WIN32_CHICAGO
#endif // NETTEST_UTILITY
//
// Initialize the DLL critsects.
//
try {
InitializeCriticalSection( &GlobalDCNameCritSect );
} except( EXCEPTION_EXECUTE_HANDLER ) {
NlPrint((0,"NETAPI32.DLL: Cannot initialize GlobalDCNameCritSect\n"));
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
}
if ( NetStatus == NO_ERROR ) {
//
// Initialize globals
//
DsGetDcWsaInitialized = FALSE;
//
// Initialize the cache of discovered domains.
//
NetStatus = NetpDcInitializeCache();
if ( NetStatus != NO_ERROR ) {
NlPrint((0,"NETAPI32.DLL: Cannot NetpDcinitializeCache\n"));
}
}
return NetStatus;
}
VOID
DCNameClose(
VOID
)
/*++
Routine Description:
Perform per-process cleanup.
Arguments:
None.
Return Value:
None.
--*/
{
#ifndef NETTEST_UTILITY
#ifndef WIN32_CHICAGO
//
// Shutdown the RPC binding cache.
//
NlBindingDetachDll();
#endif // WIN32_CHICAGO
#endif // NETTEST_UTILITY
//
// If we initialized winsock,
// cleanup.
//
LOCKDOMAINSEM();
if ( DsGetDcWsaInitialized ) {
WSACleanup();
DsGetDcWsaInitialized = FALSE;
}
UNLOCKDOMAINSEM();
//
// Delete the critsect that protects the DCName cache
//
DeleteCriticalSection( &GlobalDCNameCritSect );
//
// Free the cache of discovered DCs.
//
NetpDcUninitializeCache();
}
#if NETLOGONDBG
#define MAX_PRINTF_LEN 1024 // Arbitrary.
VOID
NlPrintRoutine(
IN DWORD DebugFlag,
IN LPSTR Format,
...
)
/*++
Routine Description:
Local version of NlPrintRoutine for those instances where we're not
compiled directly into the netlogon service.
Arguments:
Return Value:
--*/
{
#ifdef NETTEST_UTILITY
extern BOOL ReallyVerbose;
#endif // NETTEST_UTILITY
#ifndef NETTEST_UTILITY
// NetlibpTrace |= NETLIB_DEBUG_LOGON; // ?? Force verbosity
#ifndef WIN32_CHICAGO
IF_DEBUG( LOGON ) {
#endif // WIN32_CHICAGO
#endif // NETTEST_UTILITY
va_list arglist;
char OutputBuffer[MAX_PRINTF_LEN];
ULONG length = 0;
static BeginningOfLine = TRUE;
//
// Handle the beginning of a new line.
//
//
if ( BeginningOfLine ) {
#ifdef NETTEST_UTILITY
if ( ReallyVerbose ) {
printf( " " );
}
#endif // NETTEST_UTILITY
}
va_start(arglist, Format);
#ifndef WIN32_CHICAGO
length = (ULONG) vsprintf(OutputBuffer, Format, arglist);
#else // WIN32_CHICAGO
length = (ULONG) vsprintf(OutputBuffer + length - 1, Format, arglist);
#endif // WIN32_CHICAGO
va_end(arglist);
BeginningOfLine = (length > 0 && OutputBuffer[length-1] == '\n' );
// Output buffer may contain percent signs (like "%SystemRoot%"), so
// print it without parsing it.
#ifndef WIN32_CHICAGO
#ifdef NETTEST_UTILITY
if ( ReallyVerbose ) {
printf( "%s", (PCH) OutputBuffer);
}
#else NETTEST_UTILITY
(void) DbgPrint( "%s", (PCH) OutputBuffer);
#endif // NETTEST_UTILITY
#else // WIN32_CHICAGO
OutputDebugString( OutputBuffer);
#endif // WIN32_CHICAGO
#ifndef NETTEST_UTILITY
#ifndef WIN32_CHICAGO
}
#endif // WIN32_CHICAGO
#endif // NETTEST_UTILITY
}
#endif // DBG
#ifndef WIN32_CHICAGO
#if NETLOGONDBG
//
// Have my own version of RtlAssert so debug versions of netlogon really assert on
// free builds.
//
VOID
NlAssertFailed(
IN PVOID FailedAssertion,
IN PVOID FileName,
IN ULONG LineNumber,
IN PCHAR Message OPTIONAL
)
{
#ifdef NETTEST_UTILITY
NlPrint((0, "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n",
Message ? Message : "",
FailedAssertion,
FileName,
LineNumber
));
#else NETTEST_UTILITY
RtlAssert( FailedAssertion, FileName, LineNumber, Message );
#endif NETTEST_UTILITY
}
#endif // DBG
#endif //WIN32_CHICAGO
#if NETLOGONDBG
VOID
NlpDumpBuffer(
IN DWORD DebugFlag,
PVOID Buffer,
DWORD BufferSize
)
/*++
Routine Description:
Dumps the buffer content on to the debugger output.
Arguments:
DebugFlag: Debug flag to pass on to NlPrintRoutine
Buffer: buffer pointer.
BufferSize: size of the buffer.
Return Value:
none
--*/
{
#define NUM_CHARS 16
DWORD i, limit;
CHAR TextBuffer[NUM_CHARS + 1];
LPBYTE BufferPtr = Buffer;
//
// Hex dump of the bytes
//
limit = ((BufferSize - 1) / NUM_CHARS + 1) * NUM_CHARS;
for (i = 0; i < limit; i++) {
if (i < BufferSize) {
NlPrint((0,"%02x ", BufferPtr[i]));
if (BufferPtr[i] < 31 ) {
TextBuffer[i % NUM_CHARS] = '.';
} else if (BufferPtr[i] == '\0') {
TextBuffer[i % NUM_CHARS] = ' ';
} else {
TextBuffer[i % NUM_CHARS] = (CHAR) BufferPtr[i];
}
} else {
NlPrint((0," "));
TextBuffer[i % NUM_CHARS] = ' ';
}
if ((i + 1) % NUM_CHARS == 0) {
TextBuffer[NUM_CHARS] = 0;
NlPrint((0," %s\n", TextBuffer));
}
}
UNREFERENCED_PARAMETER( DebugFlag );
}
#endif // DBG
NTSTATUS
NlBrowserSendDatagram(
IN PVOID ContextDomainInfo,
IN ULONG IpAddress,
IN LPWSTR UnicodeDestinationName,
IN DGRECEIVER_NAME_TYPE NameType,
IN LPWSTR TransportName,
IN LPSTR OemMailslotName,
IN PVOID Buffer,
IN ULONG BufferSize,
IN OUT PBOOL FlushNameOnOneIpTransport OPTIONAL
)
/*++
Routine Description:
Send the specified mailslot message to the specified mailslot on the
specified server on the specified transport..
Arguments:
DomainInfo - Hosted domain sending the datagram
IpAddress - IpAddress of the machine to send the pind to.
If zero, UnicodeDestinationName must be specified.
If ALL_IP_TRANSPORTS, UnicodeDestination must be specified but the datagram
will only be sent on IP transports.
UnicodeDestinationName -- Name of the server to send to.
NameType -- Type of name represented by UnicodeDestinationName.
TransportName -- Name of the transport to send on.
Use NULL to send on all transports.
OemMailslotName -- Name of the mailslot to send to.
Buffer -- Specifies a pointer to the mailslot message to send.
BufferSize -- Size in bytes of the mailslot message
FlushNameOnOneIpTransport -- Ignored in this implementation
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
NET_API_STATUS NetStatus;
WCHAR NetlogonMailslotName[MAX_PATH+1];
//
// Sanity check.
//
if ( ContextDomainInfo != NULL ||
TransportName != NULL ) {
NlPrint((NL_CRITICAL, "NETAPI32: NlBrowserSendDatagram internal error.\n" ));
return STATUS_INTERNAL_ERROR;
}
//
// Start building the destination mailslot name.
//
NetlogonMailslotName[0] = '\\';
NetlogonMailslotName[1] = '\\';
if ( UnicodeDestinationName != NULL ) {
wcscpy(NetlogonMailslotName + 2, UnicodeDestinationName );
} else {
NlPrint((NL_CRITICAL, "NETAPI32: NlBrowserSendDatagram internal error 2.\n" ));
return STATUS_INTERNAL_ERROR;
}
switch ( NameType ) {
case PrimaryDomain: // Primary domain (signature 0), group
break;
case DomainName: // DC Domain name (domain name, signature 1c)
wcscat( NetlogonMailslotName, L"*" );
break;
case PrimaryDomainBrowser: // PDC Browser name (domain name, signature 1b), unique
wcscat( NetlogonMailslotName, L"**" );
break;
default:
return STATUS_INVALID_PARAMETER;
}
NetpCopyStrToWStr( &NetlogonMailslotName[wcslen(NetlogonMailslotName)],
OemMailslotName );
NetStatus = NetpLogonWriteMailslot(
NetlogonMailslotName,
Buffer,
BufferSize );
#ifndef WIN32_CHICAGO
NlPrint((NL_MAILSLOT,
"Sent out '%s' message to %ws on all transports.\n",
NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)Buffer)->Opcode),
NetlogonMailslotName));
#endif // WIN32_CHICAGO
#if NETLOGONDBG
NlpDumpBuffer( NL_MAILSLOT_TEXT, Buffer, BufferSize );
#endif // NETLOGONDBG
if ( NetStatus != NERR_Success ) {
Status = NetpApiStatusToNtStatus( NetStatus );
NlPrint(( NL_CRITICAL,
"NetpDcSendPing: cannot write netlogon mailslot: %ws 0x%lx %ld\n",
UnicodeDestinationName,
IpAddress,
NetStatus));
} else {
Status = STATUS_SUCCESS;
}
NlPrint(( NL_MISC, "NlBrowserSendDatagram : returned 0x%lx\n", NetStatus));
return Status;
}
NET_API_STATUS
DsWsaInitialize(
VOID
)
/*++
Routine Description:
Initialize winsock.
Arguments:
None.
Return Value:
Status of the operation.
--*/
{
NET_API_STATUS NetStatus;
WORD wVersionRequested;
WSADATA wsaData;
int err;
LOCKDOMAINSEM();
if ( !DsGetDcWsaInitialized ) {
//
// Initialize winsock.
//
wVersionRequested = MAKEWORD( 1, 1 );
NetStatus = WSAStartup( wVersionRequested, &wsaData );
if ( NetStatus != 0 ) {
UNLOCKDOMAINSEM();
NlPrint((NL_CRITICAL, "NETAPI32.DLL: Cannot initialize winsock %ld.\n", NetStatus ));
return NetStatus;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup();
UNLOCKDOMAINSEM();
NlPrint((NL_CRITICAL, "NETAPI32.DLL: Wrong winsock version %ld.\n", wsaData.wVersion ));
return WSANOTINITIALISED;
}
DsGetDcWsaInitialized = TRUE;
}
UNLOCKDOMAINSEM();
return NO_ERROR;
}
DWORD
DsWsaGetDcName(
IN LPCWSTR ComputerName OPTIONAL,
IN LPCWSTR AccountName OPTIONAL,
IN ULONG AllowableAccountControlBits,
IN LPCWSTR DomainName OPTIONAL,
IN GUID *DomainGuid OPTIONAL,
IN LPCWSTR SiteName OPTIONAL,
IN ULONG Flags,
IN ULONG InternalFlags,
IN DWORD Timeout,
IN LPWSTR NetbiosPrimaryDomainName OPTIONAL,
IN LPWSTR DnsPrimaryDomainName OPTIONAL,
IN GUID *PrimaryDomainGuid OPTIONAL,
OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
)
/*++
Routine Description:
Wrapper for DsIGetDcName that ensures WSA has been initialized.
i
Arguments:
(See DsIGetDcName).
Return Value:
(See DsIGetDcName).
--*/
{
NET_API_STATUS NetStatus;
//
// Pass the call through to DsIGetDcName
//
NetStatus = DsIGetDcName(
ComputerName,
AccountName,
AllowableAccountControlBits,
DomainName,
NULL, // Tree name is not known
DomainGuid,
SiteName,
Flags,
InternalFlags,
NULL, // No send datagram context
Timeout,
NetbiosPrimaryDomainName,
DnsPrimaryDomainName,
PrimaryDomainGuid,
NULL,
NULL,
DomainControllerInfo );
//
// If Winsock has not yet been initialized,
// initialize it.
//
if ( NetStatus == WSANOTINITIALISED ) {
//
// Initialize WSA.
//
NetStatus = DsWsaInitialize();
if ( NetStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Repeat the call.
//
NetStatus = DsIGetDcName(
ComputerName,
AccountName,
AllowableAccountControlBits,
DomainName,
NULL, // Tree name is not known
DomainGuid,
SiteName,
Flags,
InternalFlags,
NULL, // No send datagram context
Timeout,
NetbiosPrimaryDomainName,
DnsPrimaryDomainName,
PrimaryDomainGuid,
NULL,
NULL,
DomainControllerInfo );
}
//
// Free locally used resouces.
//
Cleanup:
return NetStatus;
}
DWORD
DsLocalGetDcName(
IN LPCWSTR ComputerName OPTIONAL,
IN LPCWSTR AccountName OPTIONAL,
IN ULONG AllowableAccountControlBits,
IN LPCWSTR DomainName OPTIONAL,
IN GUID *DomainGuid OPTIONAL,
IN LPCWSTR SiteName OPTIONAL,
IN ULONG Flags,
IN ULONG InternalFlags,
IN DWORD Timeout,
IN LPWSTR NetbiosPrimaryDomainName OPTIONAL,
IN LPWSTR DnsPrimaryDomainName OPTIONAL,
IN LPWSTR DnsPrimaryForestName OPTIONAL,
IN GUID *PrimaryDomainGuid OPTIONAL,
OUT PBOOLEAN Local,
OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
)
/*++
Routine Description:
Wrapper for DsWsaGetDcName that ensures this is a local call.
Arguments:
(See DsIGetDcName).
Local - Returns TRUE if the call is local and this routine performed
the requested operation.
Return Value:
(See DsIGetDcName).
--*/
{
NET_API_STATUS NetStatus;
DWORD LocalOrRemote;
//
// If no computername was specified,
// mark this as a local call.
//
*Local = TRUE;
#ifndef WIN32_CHICAGO
if ( ComputerName == NULL || ComputerName[0] == '\0' ) {
LocalOrRemote = ISLOCAL;
//
// Check if the ComputerName specifies this machine.
//
} else {
NetStatus = NetpIsRemote(
(LPWSTR) ComputerName, // uncanon server name
&LocalOrRemote,
NULL,
0 );
if (NetStatus != NERR_Success) {
goto Cleanup;
}
}
#else // WIN32_CHICAGO
LocalOrRemote = ISLOCAL;
#endif // WIN32_CHICAGO
//
// If the call is local,
// just do it.
//
if ( LocalOrRemote == ISLOCAL ) {
//
// Initialize WSA.
// (Ignore errors since WSA isn't always needed.)
//
(VOID) DsWsaInitialize();
NetStatus = DsIGetDcName(
NULL, // Don't use computer name. This has to be netbios name
AccountName,
AllowableAccountControlBits,
DomainName,
DnsPrimaryForestName,
DomainGuid,
SiteName,
Flags,
InternalFlags,
NULL, // No send datagram context
Timeout,
NetbiosPrimaryDomainName,
DnsPrimaryDomainName,
PrimaryDomainGuid,
NULL,
NULL,
DomainControllerInfo );
} else {
*Local = FALSE;
NetStatus = NO_ERROR;
}
//
// Cleanup all locally used resources
//
#ifndef WIN32_CHICAGO
Cleanup:
#endif // WIN32_CHICAGO
return NetStatus;
}
DWORD
WINAPI
DsGetDcOpenW(
IN LPCWSTR DnsName,
IN ULONG OptionFlags,
IN LPCWSTR SiteName OPTIONAL,
IN GUID *DomainGuid OPTIONAL,
IN LPCWSTR DnsForestName OPTIONAL,
IN ULONG DcFlags,
OUT PHANDLE RetGetDcContext
)
/*++
Routine Description:
Open a context for retrieval of the addresses of machines that have
registered LDAP.TCP.<xxx> SRV records.
Arguments:
DnsName - Unicode DNS name of the LDAP server to lookup
OptionFlags - Flags affecting the operation of the routine.
DS_ONLY_DO_SITE_NAME - Non-site names should be ignored.
DS_NOTIFY_AFTER_SITE_RECORDS - Return ERROR_FILEMARK_DETECTED
after all site specific records have been processed.
SiteName - Name of site the client is in.
DomainGuid - Specifies the GUID of the domain specified by DnsName.
This value is used to handle the case of domain renames. If this
value is specified and DomainName has been renamed, DsGetDcName will
attempt to locate a DC in the domain having this specified DomainGuid.
DnsForestName - Specifies the name of the domain at the root of the tree
containing DnsName. This value is used in conjunction with DomainGuid
for finding DnsName if the domain has been renamed.
DcFlags - Passes additional information to be used to process the request.
DcFlags can be a combination values bitwise or'ed together.
Any of the following flags are allowed and have the same meaning as
for DsGetDcName:
DS_PDC_REQUIRED
DS_GC_SERVER_REQUIRED
DS_WRITABLE_REQUIRED
DS_FORCE_REDISCOVERY - Avoids DNS cache
If no flags are specified, no special DC role is required.
RetGetDcContext - Returns an opaque context.
This context must be freed using DsGetDcClose.
Return Value:
Status of the operation.
NO_ERROR: GetDcContext was returned successfully.
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
LPSTR DnsNameUtf8 = NULL;
LPSTR DnsForestNameUtf8 = NULL;
//
// Validate the input
//
if ( DnsName == NULL || *DnsName == UNICODE_NULL ) {
NetStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
if ( RetGetDcContext == NULL ) {
NetStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Convert DnsName and DnsForestName to UTF-8
//
DnsNameUtf8 = NetpAllocUtf8StrFromWStr( DnsName );
if ( DnsNameUtf8 == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
if ( DnsForestName != NULL ) {
DnsForestNameUtf8 = NetpAllocUtf8StrFromWStr( DnsForestName );
if ( DnsForestNameUtf8 == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
NetStatus = NetpDcGetDcOpen( DnsNameUtf8,
OptionFlags,
SiteName,
DomainGuid,
DnsForestNameUtf8,
DcFlags,
RetGetDcContext );
Cleanup:
if ( DnsNameUtf8 != NULL ) {
NetpMemoryFree( DnsNameUtf8 );
}
if ( DnsForestNameUtf8 != NULL ) {
NetpMemoryFree( DnsForestNameUtf8 );
}
return NetStatus;
}
DWORD
WINAPI
DsGetDcOpenA(
IN LPCSTR DnsName,
IN ULONG OptionFlags,
IN LPCSTR SiteName OPTIONAL,
IN GUID *DomainGuid OPTIONAL,
IN LPCSTR DnsForestName OPTIONAL,
IN ULONG DcFlags,
OUT PHANDLE RetGetDcContext
)
/*++
Routine Description:
Open a context for retrieval of the addresses of machines that have
registered LDAP.TCP.<xxx> SRV records.
Arguments:
DnsName - Unicode DNS name of the LDAP server to lookup
OptionFlags - Flags affecting the operation of the routine.
DS_ONLY_DO_SITE_NAME - Non-site names should be ignored.
DS_NOTIFY_AFTER_SITE_RECORDS - Return ERROR_FILEMARK_DETECTED
after all site specific records have been processed.
SiteName - Name of site the client is in.
DomainGuid - Specifies the GUID of the domain specified by DnsName.
This value is used to handle the case of domain renames. If this
value is specified and DomainName has been renamed, DsGetDcName will
attempt to locate a DC in the domain having this specified DomainGuid.
DnsForestName - Specifies the name of the domain at the root of the tree
containing DnsName. This value is used in conjunction with DomainGuid
for finding DnsName if the domain has been renamed.
DcFlags - Passes additional information to be used to process the request.
DcFlags can be a combination values bitwise or'ed together.
Any of the following flags are allowed and have the same meaning as
for DsGetDcName:
DS_PDC_REQUIRED
DS_GC_SERVER_REQUIRED
DS_WRITABLE_REQUIRED
DS_FORCE_REDISCOVERY - Avoids DNS cache
If no flags are specified, no special DC role is required.
RetGetDcContext - Returns an opaque context.
This context must be freed using DsGetDcClose.
Return Value:
Status of the operation.
NO_ERROR: GetDcContext was returned successfully.
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
LPWSTR DnsNameW = NULL;
LPWSTR SiteNameW = NULL;
LPWSTR DnsForestNameW = NULL;
//
// Convert DnsName and DnsForestName to UTF-8
//
if ( DnsName != NULL && *DnsName != '\0' ) {
DnsNameW = NetpAllocWStrFromAStr( DnsName );
if ( DnsNameW == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
if ( SiteName != NULL && *SiteName != '\0') {
SiteNameW = NetpAllocWStrFromAStr( SiteName );
if ( SiteNameW == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
if ( DnsForestName != NULL && *DnsForestName != '\0' ) {
DnsForestNameW = NetpAllocWStrFromAStr( DnsForestName );
if ( DnsForestNameW == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
NetStatus = DsGetDcOpenW( DnsNameW,
OptionFlags,
SiteNameW,
DomainGuid,
DnsForestNameW,
DcFlags,
RetGetDcContext );
Cleanup:
if ( DnsNameW != NULL ) {
NetApiBufferFree( DnsNameW );
}
if ( SiteNameW != NULL ) {
NetApiBufferFree( SiteNameW );
}
if ( DnsForestNameW != NULL ) {
NetApiBufferFree( DnsForestNameW );
}
return NetStatus;
}
DWORD
WINAPI
DsGetDcNextW(
IN HANDLE GetDcContextHandle,
OUT PULONG SockAddressCount OPTIONAL,
OUT LPSOCKET_ADDRESS *SockAddresses OPTIONAL,
OUT LPWSTR *DnsHostName OPTIONAL
)
/*++
Routine Description:
Returns the next logical SRV record for the name opened by DsGetDcOpen.
The returned record takes into account the weights and priorities specified
in the SRV records.
Arguments:
GetDcContextHandle - An opaque context describing the SRV records.
SockAddressCount - Returns the number of Addresses in SockAddresses.
If NULL, addresses will not be looked up.
SockAddresses - Returns an array SOCKET_ADDRESS structures for the server.
All returned addresses will be of family AF_INET or AF_INET6.
The returned sin_port field contains port from the SRV record.
A Port of 0 indicate no port is available from DNS.
This buffer should be freed using LocalFree().
DnsHostName - Returns a pointer to the DnsHostName in the SRV record.
A NULL is returned if no host name is known. Must be freed by
calling NetApiBufferFree.
Return Value:
NO_ERROR: Addresses were returned
ERROR_NO_MORE_ITEMS: No more addresses are available.
ERROR_FILEMARK_DETECTED: Caller has specified the DS_NOTIFY_AFTER_SITE_RECORDS flag
and DsGetDcNext has processed all of the site specific SRV records. The caller
should take any action based on no site specific DCs being available, then
should call DsGetDcNext to continue on to other DCs.
Any other errors returned are those detected while trying to find the A
records associated with the host of the SRV record. The caller can
note the error (perhaps so the caller can return this status to
his caller if no usefull server is found) then call DsGetDcNext
again to get the next SRV record. The caller can inspect this error
and return immediately if the caller deems the error serious.
The following interesting errors might be returned:
DNS_ERROR_RCODE_NAME_ERROR: No A records are available for this SRV record.
ERROR_TIMEOUT: DNS server didn't respond in a reasonable time
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
LPSTR DnsHostNameUtf8 = NULL;
LPWSTR LocalDnsHostName = NULL;
ULONG LocalSockAddressCount = 0;
LPSOCKET_ADDRESS LocalSockAddresses = NULL;
//
// Call the internal version
//
NetStatus = NetpDcGetDcNext( GetDcContextHandle,
SockAddressCount != NULL ?
&LocalSockAddressCount :
NULL,
SockAddresses != NULL ?
&LocalSockAddresses :
NULL,
DnsHostName != NULL ?
&DnsHostNameUtf8 :
NULL );
if ( NetStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Convert the host name to Unicode, if needed
//
if ( DnsHostName != NULL ) {
LocalDnsHostName = NetpAllocWStrFromUtf8Str( DnsHostNameUtf8 );
if ( LocalDnsHostName == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
Cleanup:
//
// Return the data on success or clean up on error
// (No need to free DnsHostNameUtf8 as it isn't allocated)
//
if ( NetStatus == NO_ERROR ) {
if ( SockAddressCount != NULL ) {
*SockAddressCount = LocalSockAddressCount;
}
if ( SockAddresses != NULL ) {
*SockAddresses = LocalSockAddresses;
}
if ( DnsHostName != NULL ) {
*DnsHostName = LocalDnsHostName;
}
} else {
if ( LocalSockAddresses != NULL ) {
LocalFree( LocalSockAddresses );
}
if ( LocalDnsHostName != NULL ) {
NetApiBufferFree( LocalDnsHostName );
}
}
return NetStatus;
}
DWORD
WINAPI
DsGetDcNextA(
IN HANDLE GetDcContextHandle,
OUT PULONG SockAddressCount OPTIONAL,
OUT LPSOCKET_ADDRESS *SockAddresses OPTIONAL,
OUT LPSTR *DnsHostName OPTIONAL
)
/*++
Routine Description:
Returns the next logical SRV record for the name opened by DsGetDcOpen.
The returned record takes into account the weights and priorities specified
in the SRV records.
Arguments:
GetDcContextHandle - An opaque context describing the SRV records.
SockAddressCount - Returns the number of Addresses in SockAddresses.
If NULL, addresses will not be looked up.
SockAddresses - Returns an array SOCKET_ADDRESS structures for the server.
All returned addresses will be of family AF_INET or AF_INET6.
The returned sin_port field contains port from the SRV record.
A Port of 0 indicate no port is available from DNS.
This buffer should be freed using LocalFree().
DnsHostName - Returns a pointer to the DnsHostName in the SRV record.
A NULL is returned if no host name is known. Must be freed by
calling NetApiBufferFree.
Return Value:
NO_ERROR: Addresses were returned
ERROR_NO_MORE_ITEMS: No more addresses are available.
ERROR_FILEMARK_DETECTED: Caller has specified the DS_NOTIFY_AFTER_SITE_RECORDS flag
and DsGetDcNext has processed all of the site specific SRV records. The caller
should take any action based on no site specific DCs being available, then
should call DsGetDcNext to continue on to other DCs.
Any other errors returned are those detected while trying to find the A
records associated with the host of the SRV record. The caller can
note the error (perhaps so the caller can return this status to
his caller if no usefull server is found) then call DsGetDcNext
again to get the next SRV record. The caller can inspect this error
and return immediately if the caller deems the error serious.
The following interesting errors might be returned:
DNS_ERROR_RCODE_NAME_ERROR: No A records are available for this SRV record.
ERROR_TIMEOUT: DNS server didn't respond in a reasonable time
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
LPWSTR DnsHostNameW = NULL;
LPSTR LocalDnsHostName = NULL;
ULONG LocalSockAddressCount = 0;
LPSOCKET_ADDRESS LocalSockAddresses = NULL;
//
// Call the Unicode version
//
NetStatus = DsGetDcNextW( GetDcContextHandle,
SockAddressCount != NULL ?
&LocalSockAddressCount :
NULL,
SockAddresses != NULL ?
&LocalSockAddresses :
NULL,
DnsHostName != NULL ?
&DnsHostNameW :
NULL );
if ( NetStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Convert the host name to Unicode, if needed
//
if ( DnsHostName != NULL ) {
LocalDnsHostName = NetpAllocAStrFromWStr( DnsHostNameW );
if ( LocalDnsHostName == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
Cleanup:
if ( DnsHostNameW != NULL ) {
NetApiBufferFree( DnsHostNameW );
}
//
// Return the data on success or clean up on error
//
if ( NetStatus == NO_ERROR ) {
if ( SockAddressCount != NULL ) {
*SockAddressCount = LocalSockAddressCount;
}
if ( SockAddresses != NULL ) {
*SockAddresses = LocalSockAddresses;
}
if ( DnsHostName != NULL ) {
*DnsHostName = LocalDnsHostName;
}
} else {
if ( LocalSockAddresses != NULL ) {
LocalFree( LocalSockAddresses );
}
if ( LocalDnsHostName != NULL ) {
NetApiBufferFree( LocalDnsHostName );
}
}
return NetStatus;
}
VOID
WINAPI
DsGetDcCloseW(
IN HANDLE GetDcContextHandle
)
/*++
Routine Description:
Free the context allocated by DsGetDcOpen
Arguments:
GetDcContextHandle - An opaque context describing the SRV records.
Return Value:
None
--*/
{
//
// Just call the internal version
//
NetpDcGetDcClose( GetDcContextHandle );
}
DWORD
WINAPI
DsGetDcNameA(
IN LPCSTR ComputerName OPTIONAL,
IN LPCSTR DomainName OPTIONAL,
IN GUID *DomainGuid OPTIONAL,
IN LPCSTR SiteName OPTIONAL,
IN ULONG Flags,
OUT PDOMAIN_CONTROLLER_INFOA *DomainControllerInfo
)
/*++
Routine Description:
Same as DsGetDcNameW except it takes and returns ASCII.
Arguments:
Same as DsGetDcNameW except it takes and returns ASCII.
Return Value:
Same as DsGetDcNameW except it takes and returns ASCII.
--*/
{
return DsGetDcNameWithAccountA( ComputerName,
NULL,
0,
DomainName,
DomainGuid,
SiteName,
Flags,
DomainControllerInfo );
}
DWORD
WINAPI
DsGetDcNameWithAccountA(
IN LPCSTR ComputerName OPTIONAL,
IN LPCSTR AccountName OPTIONAL,
IN ULONG AllowableAccountControlBits,
IN LPCSTR DomainName OPTIONAL,
IN GUID *DomainGuid OPTIONAL,
IN LPCSTR SiteName OPTIONAL,
IN ULONG Flags,
OUT PDOMAIN_CONTROLLER_INFOA *DomainControllerInfo
)
/*++
Routine Description:
Same as DsGetDcNameW except it takes and returns ASCII.
Arguments:
Same as DsGetDcNameW except it takes and returns ASCII.
Return Value:
Same as DsGetDcNameW except it takes and returns ASCII.
--*/
{
DWORD WinStatus;
LPWSTR UnicodeComputerName = NULL;
LPWSTR UnicodeAccountName = NULL;
LPWSTR UnicodeDomainName = NULL;
LPWSTR UnicodeSiteName = NULL;
LPSTR AnsiDomainControllerName = NULL;
ULONG AnsiDomainControllerNameSize = 0;
LPSTR AnsiDomainControllerAddress = NULL;
ULONG AnsiDomainControllerAddressSize = 0;
LPSTR AnsiDomainName = NULL;
ULONG AnsiDomainNameSize = 0;
LPSTR AnsiForestName = NULL;
ULONG AnsiForestNameSize = 0;
LPSTR AnsiDcSiteName = NULL;
ULONG AnsiDcSiteNameSize = 0;
LPSTR AnsiClientSiteName = NULL;
ULONG AnsiClientSiteNameSize = 0;
CHAR szBuf[] = "";
LPBYTE Where;
PDOMAIN_CONTROLLER_INFOW DomainControllerInfoW = NULL;
//
// Convert input parameters to Unicode.
//
*DomainControllerInfo = NULL;
if ( ComputerName != NULL ) {
UnicodeComputerName = NetpAllocWStrFromAStr( ComputerName );
if ( UnicodeComputerName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
if ( AccountName != NULL ) {
UnicodeAccountName = NetpAllocWStrFromAStr( AccountName );
if ( UnicodeAccountName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
if ( DomainName != NULL ) {
UnicodeDomainName = NetpAllocWStrFromAStr( DomainName );
if ( UnicodeDomainName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
if ( SiteName != NULL ) {
UnicodeSiteName = NetpAllocWStrFromAStr( SiteName );
if ( UnicodeSiteName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
//
// Call the Unicode version of the routine.
//
WinStatus = DsGetDcNameWithAccountW(
UnicodeComputerName,
UnicodeAccountName,
AllowableAccountControlBits,
UnicodeDomainName,
DomainGuid,
UnicodeSiteName,
Flags,
&DomainControllerInfoW );
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Convert the output structure to Ansi character set.
//
if ( DomainControllerInfoW->DomainControllerName != NULL ) {
AnsiDomainControllerName = NetpAllocAStrFromWStr( DomainControllerInfoW->DomainControllerName );
if ( AnsiDomainControllerName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
AnsiDomainControllerNameSize = lstrlenA( AnsiDomainControllerName ) + 1;
}
if ( DomainControllerInfoW->DomainControllerAddress != NULL ) {
AnsiDomainControllerAddress = NetpAllocAStrFromWStr( DomainControllerInfoW->DomainControllerAddress );
if ( AnsiDomainControllerAddress == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
AnsiDomainControllerAddressSize = lstrlenA( AnsiDomainControllerAddress ) + 1;
}
if ( DomainControllerInfoW->DomainName != NULL ) {
AnsiDomainName = NetpAllocAStrFromWStr( DomainControllerInfoW->DomainName );
if ( AnsiDomainName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
AnsiDomainNameSize = lstrlenA( AnsiDomainName ) + 1;
}
if ( DomainControllerInfoW->DnsForestName != NULL ) {
AnsiForestName = NetpAllocAStrFromWStr( DomainControllerInfoW->DnsForestName );
if ( AnsiForestName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
AnsiForestNameSize = lstrlenA( AnsiForestName ) + 1;
}
if ( DomainControllerInfoW->DcSiteName != NULL ) {
AnsiDcSiteName = NetpAllocAStrFromWStr( DomainControllerInfoW->DcSiteName );
if ( AnsiDcSiteName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
AnsiDcSiteNameSize = lstrlenA( AnsiDcSiteName ) + 1;
}
if ( DomainControllerInfoW->ClientSiteName != NULL ) {
AnsiClientSiteName = NetpAllocAStrFromWStr( DomainControllerInfoW->ClientSiteName );
if ( AnsiClientSiteName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
AnsiClientSiteNameSize = lstrlenA( AnsiClientSiteName ) + 1;
}
//
// Allocate the Ansi version of the structure.
//
WinStatus = NetApiBufferAllocate(
sizeof(DOMAIN_CONTROLLER_INFOA) +
AnsiDomainControllerNameSize +
AnsiDomainControllerAddressSize +
AnsiDomainNameSize +
AnsiForestNameSize +
AnsiDcSiteNameSize +
AnsiClientSiteNameSize,
DomainControllerInfo );
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
Where = (LPBYTE)((*DomainControllerInfo) + 1);
//
// Copy information into the allocated buffer.
//
*(*DomainControllerInfo) = *(PDOMAIN_CONTROLLER_INFOA)DomainControllerInfoW;
if ( AnsiDomainControllerName != NULL ) {
(*DomainControllerInfo)->DomainControllerName = Where;
RtlCopyMemory( Where,
AnsiDomainControllerName,
AnsiDomainControllerNameSize );
Where += AnsiDomainControllerNameSize;
}
if ( AnsiDomainControllerAddress != NULL ) {
(*DomainControllerInfo)->DomainControllerAddress = Where;
RtlCopyMemory( Where,
AnsiDomainControllerAddress,
AnsiDomainControllerAddressSize );
Where += AnsiDomainControllerAddressSize;
}
if ( AnsiDomainName != NULL ) {
(*DomainControllerInfo)->DomainName = Where;
RtlCopyMemory( Where,
AnsiDomainName,
AnsiDomainNameSize );
Where += AnsiDomainNameSize;
}
if ( AnsiForestName != NULL ) {
(*DomainControllerInfo)->DnsForestName = Where;
RtlCopyMemory( Where,
AnsiForestName,
AnsiForestNameSize );
Where += AnsiForestNameSize;
}
if ( AnsiDcSiteName != NULL ) {
(*DomainControllerInfo)->DcSiteName = Where;
RtlCopyMemory( Where,
AnsiDcSiteName,
AnsiDcSiteNameSize );
Where += AnsiDcSiteNameSize;
}
if ( AnsiClientSiteName != NULL ) {
(*DomainControllerInfo)->ClientSiteName = Where;
RtlCopyMemory( Where,
AnsiClientSiteName,
AnsiClientSiteNameSize );
Where += AnsiClientSiteNameSize;
}
#ifdef WIN32_CHICAGO
NlPrint((NL_MISC, "DomainControllerName: \t\t\"%s\"\n", AnsiDomainControllerName ? AnsiDomainControllerName : szBuf));
NlPrint((NL_MISC, "DomainControllerAddress:\t\t\"%s\"\n", AnsiDomainControllerAddress ? AnsiDomainControllerAddress : szBuf ));
NlPrint((NL_MISC, "DomainControllerAddressType: \t%d\n", DomainControllerInfoW->DomainControllerAddressType ));
NlPrint((NL_MISC, "DomainGuid : \t\n"));
NlPrint((NL_MISC, "DomainName: \t\t\t\"%s\"\n", AnsiDomainName ? AnsiDomainName : szBuf));
NlPrint((NL_MISC, "DnsForestName: \t\t\t\"%s\"\n", AnsiForestName ? AnsiForestName : szBuf));
NlPrint((NL_MISC, "Flags: \t\t\t\t 0x%x\n", DomainControllerInfoW->Flags));
NlPrint((NL_MISC, "DcSiteName: \t\t\t\"%s\"\n", AnsiDcSiteName ? AnsiDcSiteName : szBuf));
NlPrint((NL_MISC, "ClientSiteName: \t\t\t\"%s\"\n", AnsiClientSiteName ? AnsiClientSiteName : szBuf));
#endif // WIN32_CHICAGO
WinStatus = NO_ERROR;
//
// Clean up locally used resources.
//
Cleanup:
if ( UnicodeComputerName != NULL ) {
NetApiBufferFree( UnicodeComputerName );
}
if ( UnicodeAccountName != NULL ) {
NetApiBufferFree( UnicodeAccountName );
}
if ( UnicodeDomainName != NULL ) {
NetApiBufferFree( UnicodeDomainName );
}
if ( UnicodeSiteName != NULL ) {
NetApiBufferFree( UnicodeSiteName );
}
if ( DomainControllerInfoW != NULL ) {
NetApiBufferFree( DomainControllerInfoW );
}
if ( AnsiDomainControllerName != NULL ) {
NetApiBufferFree( AnsiDomainControllerName );
}
if ( AnsiDomainControllerAddress != NULL ) {
NetApiBufferFree( AnsiDomainControllerAddress );
}
if ( AnsiDomainName != NULL ) {
NetApiBufferFree( AnsiDomainName );
}
if ( AnsiForestName != NULL ) {
NetApiBufferFree( AnsiForestName );
}
if ( AnsiDcSiteName != NULL ) {
NetApiBufferFree( AnsiDcSiteName );
}
if ( AnsiClientSiteName != NULL ) {
NetApiBufferFree( AnsiClientSiteName );
}
if ( WinStatus != NO_ERROR ) {
if ( *DomainControllerInfo != NULL ) {
NetApiBufferFree( *DomainControllerInfo );
*DomainControllerInfo = NULL;
}
}
return WinStatus;
}
#ifndef WIN32_CHICAGO
NTSTATUS
NlWaitForEvent(
LPWSTR EventName,
ULONG Timeout
)
/*++
Routine Description:
Wait up to Timeout seconds for EventName to be triggered.
Arguments:
EventName - Name of event to wait on
Timeout - Timeout for event (in seconds).
Return Status:
STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
--*/
{
NTSTATUS Status;
HANDLE EventHandle;
OBJECT_ATTRIBUTES EventAttributes;
UNICODE_STRING EventNameString;
LARGE_INTEGER LocalTimeout;
//
// Create an event for us to wait on.
//
RtlInitUnicodeString( &EventNameString, EventName);
InitializeObjectAttributes( &EventAttributes, &EventNameString, 0, 0, NULL);
Status = NtCreateEvent(
&EventHandle,
SYNCHRONIZE,
&EventAttributes,
NotificationEvent,
(BOOLEAN) FALSE // The event is initially not signaled
);
if ( !NT_SUCCESS(Status)) {
//
// If the event already exists, the server beat us to creating it.
// Just open it.
//
if( Status == STATUS_OBJECT_NAME_EXISTS ||
Status == STATUS_OBJECT_NAME_COLLISION ) {
Status = NtOpenEvent( &EventHandle,
SYNCHRONIZE,
&EventAttributes );
}
if ( !NT_SUCCESS(Status)) {
NlPrint((0,"[NETAPI32] OpenEvent failed %lx\n", Status ));
return Status;
}
}
//
// Wait for NETLOGON to initialize. Wait a maximum of Timeout seconds.
//
LocalTimeout.QuadPart = ((LONGLONG)(Timeout)) * (-10000000);
Status = NtWaitForSingleObject( EventHandle, (BOOLEAN)FALSE, &LocalTimeout);
(VOID) NtClose( EventHandle );
if ( !NT_SUCCESS(Status) || Status == STATUS_TIMEOUT ) {
if ( Status == STATUS_TIMEOUT ) {
Status = STATUS_NETLOGON_NOT_STARTED; // Map to an error condition
}
return Status;
}
return STATUS_SUCCESS;
}
NTSTATUS
NlWaitForNetlogon(
ULONG Timeout
)
/*++
Routine Description:
Wait up to Timeout seconds for the netlogon service to start.
Arguments:
Timeout - Timeout for event (in seconds).
Return Status:
STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
--*/
{
NTSTATUS Status;
NET_API_STATUS NetStatus;
SC_HANDLE ScManagerHandle = NULL;
SC_HANDLE ServiceHandle = NULL;
SERVICE_STATUS ServiceStatus;
LPQUERY_SERVICE_CONFIG ServiceConfig;
LPQUERY_SERVICE_CONFIG AllocServiceConfig = NULL;
QUERY_SERVICE_CONFIG DummyServiceConfig;
DWORD ServiceConfigSize;
//
// If the netlogon service is currently running,
// skip the rest of the tests.
//
Status = NlWaitForEvent( L"\\NETLOGON_SERVICE_STARTED", 0 );
if ( NT_SUCCESS(Status) ) {
return Status;
}
//
// If we're in setup,
// don't bother waiting for netlogon to start.
//
if ( NlDoingSetup() ) {
return STATUS_NETLOGON_NOT_STARTED;
}
//
// Open a handle to the Netlogon Service.
//
ScManagerHandle = OpenSCManager(
NULL,
NULL,
SC_MANAGER_CONNECT );
if (ScManagerHandle == NULL) {
NlPrint((0, "[NETAPI32] NlWaitForNetlogon: OpenSCManager failed: "
"%lu\n", GetLastError()));
Status = STATUS_NETLOGON_NOT_STARTED;
goto Cleanup;
}
ServiceHandle = OpenService(
ScManagerHandle,
SERVICE_NETLOGON,
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG );
if ( ServiceHandle == NULL ) {
NlPrint((0, "[NETAPI32] NlWaitForNetlogon: OpenService failed: "
"%lu\n", GetLastError()));
Status = STATUS_NETLOGON_NOT_STARTED;
goto Cleanup;
}
//
// If the Netlogon service isn't configured to be automatically started
// by the service controller, don't bother waiting for it to start.
//
// ?? Pass "DummyServiceConfig" and "sizeof(..)" since QueryService config
// won't allow a null pointer, yet.
if ( QueryServiceConfig(
ServiceHandle,
&DummyServiceConfig,
sizeof(DummyServiceConfig),
&ServiceConfigSize )) {
ServiceConfig = &DummyServiceConfig;
} else {
NetStatus = GetLastError();
if ( NetStatus != ERROR_INSUFFICIENT_BUFFER ) {
NlPrint((0, "[NETAPI32] NlWaitForNetlogon: QueryServiceConfig failed: "
"%lu\n", NetStatus));
Status = STATUS_NETLOGON_NOT_STARTED;
goto Cleanup;
}
AllocServiceConfig = LocalAlloc( 0, ServiceConfigSize );
ServiceConfig = AllocServiceConfig;
if ( AllocServiceConfig == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
if ( !QueryServiceConfig(
ServiceHandle,
ServiceConfig,
ServiceConfigSize,
&ServiceConfigSize )) {
NlPrint((0, "[NETAPI32] NlWaitForNetlogon: QueryServiceConfig "
"failed again: %lu\n", GetLastError()));
Status = STATUS_NETLOGON_NOT_STARTED;
goto Cleanup;
}
}
if ( ServiceConfig->dwStartType != SERVICE_AUTO_START ) {
NlPrint((0, "[NETAPI32] NlWaitForNetlogon: Netlogon start type invalid:"
"%lu\n", ServiceConfig->dwStartType ));
Status = STATUS_NETLOGON_NOT_STARTED;
goto Cleanup;
}
//
// Loop waiting for the netlogon service to start.
// (Convert Timeout to a number of 10 second iterations)
//
Timeout = (Timeout+9)/10;
for (;;) {
//
// Query the status of the Netlogon service.
//
if (! QueryServiceStatus( ServiceHandle, &ServiceStatus )) {
NlPrint((0, "[NETAPI32] NlWaitForNetlogon: QueryServiceStatus failed: "
"%lu\n", GetLastError() ));
Status = STATUS_NETLOGON_NOT_STARTED;
goto Cleanup;
}
//
// Return or continue waiting depending on the state of
// the netlogon service.
//
switch( ServiceStatus.dwCurrentState) {
case SERVICE_RUNNING:
Status = STATUS_SUCCESS;
goto Cleanup;
case SERVICE_STOPPED:
//
// If Netlogon failed to start,
// error out now. The caller has waited long enough to start.
//
if ( ServiceStatus.dwWin32ExitCode != ERROR_SERVICE_NEVER_STARTED ){
#if NETLOGONDBG
NlPrint((0, "[NETAPI32] NlWaitForNetlogon: "
"Netlogon service couldn't start: %lu %lx\n",
ServiceStatus.dwWin32ExitCode,
ServiceStatus.dwWin32ExitCode ));
if ( ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR ) {
NlPrint((0, " Service specific error code: %lu %lx\n",
ServiceStatus.dwServiceSpecificExitCode,
ServiceStatus.dwServiceSpecificExitCode ));
}
#endif // DBG
Status = STATUS_NETLOGON_NOT_STARTED;
goto Cleanup;
}
//
// If Netlogon has never been started on this boot,
// continue waiting for it to start.
//
break;
//
// If Netlogon is trying to start up now,
// continue waiting for it to start.
//
case SERVICE_START_PENDING:
break;
//
// Any other state is bogus.
//
default:
NlPrint((0, "[NETAPI32] NlWaitForNetlogon: "
"Invalid service state: %lu\n",
ServiceStatus.dwCurrentState ));
Status = STATUS_NETLOGON_NOT_STARTED;
goto Cleanup;
}
//
// Wait ten seconds for the netlogon service to start.
// If it has successfully started, just return now.
//
Status = NlWaitForEvent( L"\\NETLOGON_SERVICE_STARTED", 10 );
if ( Status != STATUS_NETLOGON_NOT_STARTED ) {
goto Cleanup;
}
//
// If we've waited long enough for netlogon to start,
// time out now.
//
if ( (--Timeout) == 0 ) {
Status = STATUS_NETLOGON_NOT_STARTED;
goto Cleanup;
}
}
/* NOT REACHED */
Cleanup:
if ( ScManagerHandle != NULL ) {
(VOID) CloseServiceHandle(ScManagerHandle);
}
if ( ServiceHandle != NULL ) {
(VOID) CloseServiceHandle(ServiceHandle);
}
if ( AllocServiceConfig != NULL ) {
LocalFree( AllocServiceConfig );
}
return Status;
}
#endif // WIN32_CHICAGO
DWORD
WINAPI
#ifdef NETTEST_UTILITY
NettestDsGetDcNameW(
#else // NETTEST_UTILITY
DsGetDcNameW(
#endif // NETTEST_UTILITY
IN LPCWSTR ComputerName OPTIONAL,
IN LPCWSTR DomainName OPTIONAL,
IN GUID *DomainGuid OPTIONAL,
IN LPCWSTR SiteName OPTIONAL,
IN ULONG Flags,
OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
)
/*++
Routine Description:
The DsGetDcName API returns the name of a DC in a specified domain.
The domain may be trusted (directly or indirectly) by the caller or
may be untrusted. DC selection criteria are supplied to the API to
indicate preference for a DC with particular characteristics.
The DsGetDcName API is available in an ANSI and UNICODE versions.
This is the Unicode version.
The DsGetDcName API does not require any particular access to the
specified domain. DsGetDcName does not ensure the returned domain
controller is currently available by default. Rather, the caller
should attempt to use the returned domain controller. If the domain
controller is indeed not available, the caller should repeat the
DsGetDcName call specifying the DS_FORCE_REDISCOVERY flag.
The DsGetDcName API is remoted to the Netlogon service on the machine
specified by ComputerName.
Arguments:
ComputerName - Specifies the name of the server to remote this API to.
Typically, this parameter should be specified as NULL.
DomainName - The name of the domain to query. This name can either be a
DNS-style name (e.g., microsoft.com) or a flat-style name
(e.g., microsoft).
DomainGuid - Specifies the Domain GUID of the domain being queried.
This value is used to handle the case of domain renames. If this
value is specified and DomainName has been renamed, DsGetDcName will
attempt to locate a DC in the domain having this specified DomainGuid.
SiteName - Specifies the site name of the site the returned DC should be
"close" to. The parameter should typically be the site name of the
site the client is in. If not specified, the site name defaults to
the site of ComputerName.
Flags - Passes additional information to be used to process the request.
Flags can be a combination values bitwise or'ed together.
DomainControllerInfo - Returns a pointer to a DOMAIN_CONTROLLER_INFO
structure describing the domain controller selected. The returned
structure must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
ERROR_INVALID_DOMAINNAME - The format of the specified domain name is
invalid.
ERROR_NO_SUCH_DOMAIN: No DC is available for the specified domain or
the domain does not exist.
ERROR_NO_SUCH_USER: A DC responded that the specified user account
doesn't exist
ERROR_INVALID_FLAGS - The flags parameter has conflicting or superfluous
bits set.
ERROR_INTERNAL_ERROR: Unhandled situation detected.
Various Winsock errors.
--*/
{
NET_API_STATUS NetStatus;
return DsGetDcNameWithAccountW(
ComputerName,
NULL, // no AccountName,
0, // no AllowableAccountControlBits,
DomainName,
DomainGuid,
SiteName,
Flags,
DomainControllerInfo );
}
DWORD
WINAPI
DsGetDcNameWithAccountW(
IN LPCWSTR ComputerName OPTIONAL,
IN LPCWSTR AccountName OPTIONAL,
IN ULONG AllowableAccountControlBits,
IN LPCWSTR DomainName OPTIONAL,
IN GUID *DomainGuid OPTIONAL,
IN LPCWSTR SiteName OPTIONAL,
IN ULONG Flags,
OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
)
/*++
Routine Description:
The DsGetDcName API returns the name of a DC in a specified domain.
The domain may be trusted (directly or indirectly) by the caller or
may be untrusted. DC selection criteria are supplied to the API to
indicate preference for a DC with particular characteristics.
The DsGetDcName API is available in an ANSI and UNICODE versions.
This is the Unicode version.
The DsGetDcName API does not require any particular access to the
specified domain. DsGetDcName does not ensure the returned domain
controller is currently available by default. Rather, the caller
should attempt to use the returned domain controller. If the domain
controller is indeed not available, the caller should repeat the
DsGetDcName call specifying the DS_FORCE_REDISCOVERY flag.
The DsGetDcName API is remoted to the Netlogon service on the machine
specified by ComputerName.
Arguments:
ComputerName - Specifies the name of the server to remote this API to.
Typically, this parameter should be specified as NULL.
AccountName - Account name to pass on the ping request.
If NULL, no account name will be sent.
AllowableAccountControlBits - Mask of allowable account types for AccountName.
Valid bits are those specified by UF_MACHINE_ACCOUNT_MASK.
Invalid bits are ignored. If more than one bit is specified, the
account can be of any of the specified types.
DomainName - The name of the domain to query. This name can either be a
DNS-style name (e.g., microsoft.com) or a flat-style name
(e.g., microsoft).
DomainGuid - Specifies the Domain GUID of the domain being queried.
This value is used to handle the case of domain renames. If this
value is specified and DomainName has been renamed, DsGetDcName will
attempt to locate a DC in the domain having this specified DomainGuid.
SiteName - Specifies the site name of the site the returned DC should be
"close" to. The parameter should typically be the site name of the
site the client is in. If not specified, the site name defaults to
the site of ComputerName.
Flags - Passes additional information to be used to process the request.
Flags can be a combination values bitwise or'ed together.
DomainControllerInfo - Returns a pointer to a DOMAIN_CONTROLLER_INFO
structure describing the domain controller selected. The returned
structure must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
ERROR_INVALID_DOMAINNAME - The format of the specified domain name is
invalid.
ERROR_NO_SUCH_DOMAIN: No DC is available for the specified domain or
the domain does not exist.
ERROR_NO_SUCH_USER: A DC responded that the specified user account
doesn't exist
ERROR_INVALID_FLAGS - The flags parameter has conflicting or superfluous
bits set.
ERROR_INTERNAL_ERROR: Unhandled situation detected.
Various Winsock errors.
--*/
{
NET_API_STATUS NetStatus;
GUID *PrimaryDomainGuid = NULL;
LPWSTR NetbiosPrimaryDomainName = NULL;
LPWSTR DnsPrimaryDomainName = NULL;
LPWSTR DnsPrimaryForestName = NULL;
BOOLEAN IsWorkgroupName;
BOOLEAN Local;
//
// Determine the PrimaryDomainName
//
#ifdef WIN32_CHICAGO
NetStatus = NetpGetDomainNameExEx( &NetbiosPrimaryDomainName,
&DnsPrimaryDomainName,
&IsWorkgroupName );
#else // WIN32_CHICAGO
NetStatus = NetpGetDomainNameExExEx( &NetbiosPrimaryDomainName,
&DnsPrimaryDomainName,
&DnsPrimaryForestName,
&PrimaryDomainGuid,
&IsWorkgroupName );
#endif // WIN32_CHICAGO
if ( NetStatus != NERR_Success ) {
NlPrint(( 0, "DsGetDcNameW: cannot call NetpGetDomainName: %ld\n",
NetStatus));
goto Cleanup;
}
#ifdef WIN32_CHICAGO
IsWorkgroupName = TRUE;
#endif // WIN32_CHICAGO
#ifdef NETTEST_UTILITY
IsWorkgroupName = TRUE;
#endif // NETTEST_UTILITY
//
// Sanity check the domain name since LSA doesn't and I'll AV below
//
if ( NetbiosPrimaryDomainName != NULL &&
wcslen( NetbiosPrimaryDomainName) > DNLEN ) {
NlPrint(( 0, "DsGetDcNameW: Workgroup name is too long: %ld\n",
wcslen( NetbiosPrimaryDomainName ) ));
NetStatus = ERROR_INVALID_DOMAINNAME;
goto Cleanup;
}
//
// If this machine is a member of a workgroup (not a domain),
// and the API isn't remoted,
// do the algorithm here.
// Netlogon isn't running on this machine.
//
if ( IsWorkgroupName ) {
DWORD Timeout = NL_DC_MAX_TIMEOUT; // 15 seconds
DWORD DialUpDelayInSeconds;
//
// Read the dial up delay from the registry. Add it to the
// maximum DC discovery timeout to account for dial up
// connections. If the value cannot be read, ignore it.
//
if ( NlReadDwordNetlogonRegValue("ExpectedDialupDelay",
&DialUpDelayInSeconds) ) {
NlPrint(( 0, "DsGetDcNameWithAccountW: Read dial up delay of %ld seconds\n",
DialUpDelayInSeconds ));
Timeout += DialUpDelayInSeconds * 1000;
}
NetStatus = DsLocalGetDcName(
ComputerName,
AccountName,
AllowableAccountControlBits,
DomainName,
DomainGuid,
SiteName,
Flags,
DS_PRIMARY_NAME_IS_WORKGROUP,
Timeout,
NetbiosPrimaryDomainName,
DnsPrimaryDomainName,
DnsPrimaryForestName,
PrimaryDomainGuid,
&Local,
DomainControllerInfo );
//
// If the call was performed locally,
// We're all done regardless of the status.
//
if ( Local ) {
goto Cleanup;
}
}
#ifndef NETTEST_UTILITY
#ifndef WIN32_CHICAGO
//
// Remote the API to the Netlogon service.
//
// Do the RPC call with an exception handler since RPC will raise an
// exception if anything fails. It is up to us to figure out what
// to do once the exception is raised.
//
RpcTryExcept {
//
// Call RPC version of the API.
//
*DomainControllerInfo = NULL; // Force RPC to allocate
NetStatus = DsrGetDcNameEx2(
(LPWSTR) ComputerName,
(LPWSTR) AccountName,
AllowableAccountControlBits,
(LPWSTR) DomainName,
DomainGuid,
(LPWSTR) SiteName,
Flags,
DomainControllerInfo );
} RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) ) {
NetStatus = RpcExceptionCode();
} RpcEndExcept;
//
// If the netlogon service isn't running,
// and it's in the process of starting,
// wait for it to start.
//
if ( NetStatus == RPC_S_UNKNOWN_IF ) {
NTSTATUS TempStatus;
TempStatus = NlWaitForNetlogon( 90 );
if ( NT_SUCCESS(TempStatus) ) {
RpcTryExcept {
//
// Call RPC version of the API.
//
*DomainControllerInfo = NULL; // Force RPC to allocate
NetStatus = DsrGetDcNameEx2(
(LPWSTR) ComputerName,
(LPWSTR) AccountName,
AllowableAccountControlBits,
(LPWSTR) DomainName,
DomainGuid,
(LPWSTR) SiteName,
Flags,
DomainControllerInfo );
} RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) ) {
NetStatus = RpcExceptionCode();
} RpcEndExcept;
}
}
//
// If Netlogon isn't running on the local machine,
// or Netlogon is the NT 4.0 version of netlogon (the upgrade case),
// try doing the call in-process.
//
if ( NetStatus == RPC_S_UNKNOWN_IF ||
NetStatus == RPC_S_PROCNUM_OUT_OF_RANGE ) {
NET_API_STATUS TempStatus;
DWORD Timeout = NL_DC_MAX_TIMEOUT; // 15 seconds
DWORD DialUpDelayInSeconds;
//
// Read the dial up delay from the registry. Add it to the
// maximum DC discovery timeout to account for dial up
// connections. If the value cannot be read, ignore it.
//
if ( NlReadDwordNetlogonRegValue("ExpectedDialupDelay",
&DialUpDelayInSeconds) ) {
NlPrint(( 0, "DsGetDcNameWithAccountW: Read dial up delay of %ld seconds\n",
DialUpDelayInSeconds ));
Timeout += DialUpDelayInSeconds * 1000;
}
TempStatus = DsLocalGetDcName(
ComputerName,
AccountName,
AllowableAccountControlBits,
DomainName,
DomainGuid,
SiteName,
Flags,
IsWorkgroupName ?
DS_PRIMARY_NAME_IS_WORKGROUP : 0,
Timeout,
NetbiosPrimaryDomainName,
DnsPrimaryDomainName,
DnsPrimaryForestName,
PrimaryDomainGuid,
&Local,
DomainControllerInfo );
//
// If the call was performed locally,
// We're all done regardless of the status.
//
if ( Local ) {
NetStatus = TempStatus;
goto Cleanup;
}
}
#endif // WIN32_CHICAGO
#endif // NETTEST_UTILITY
IF_DEBUG( LOGON ) {
#ifndef WIN32_CHICAGO
NetpKdPrint(("DsrGetDcName rc = %lu 0x%lx\n",
NetStatus, NetStatus));
#else // WIN32_CHICAGO
NlPrint((0, "DsrGetDcName rc = %lu 0x%lx\n",
NetStatus, NetStatus));
#endif // WIN32_CHICAGO
}
Cleanup:
//
// Cleanup all locally used resources
//
if ( NetbiosPrimaryDomainName != NULL ) {
NetApiBufferFree( NetbiosPrimaryDomainName );
}
if ( DnsPrimaryDomainName != NULL ) {
NetApiBufferFree( DnsPrimaryDomainName );
}
if ( DnsPrimaryForestName != NULL ) {
NetApiBufferFree( DnsPrimaryForestName );
}
if ( PrimaryDomainGuid != NULL ) {
NetApiBufferFree( PrimaryDomainGuid );
}
#ifdef WIN32_CHICAGO
NlPrint((NL_MISC, "DsGetDcNameWithAccountW rc = %lu 0x%lx\n",
NetStatus, NetStatus));
#endif // WIN32_CHICAGO
return NetStatus;
}
#ifndef NETTEST_UTILITY
#ifndef WIN32_CHICAGO
NET_API_STATUS NET_API_FUNCTION
NetGetDCName (
IN LPCWSTR ServerName OPTIONAL,
IN LPCWSTR DomainName OPTIONAL,
OUT LPBYTE *Buffer
)
/*++
Routine Description:
Get the name of the primary domain controller for a domain.
Arguments:
ServerName - name of remote server (null for local)
DomainName - name of domain (null for primary)
Buffer - Returns a pointer to an allcated buffer containing the
servername of the PDC of the domain. The server name is prefixed
by \\. The buffer should be deallocated using NetApiBufferFree.
Return Value:
NERR_Success - Success. Buffer contains PDC name prefixed by \\.
NERR_DCNotFound No DC found for this domain.
ERROR_INVALID_NAME Badly formed domain name
--*/
{
NET_API_STATUS NetStatus = 0;
PDOMAIN_CONTROLLER_INFOW DomainControllerInfo = NULL;
//
// API SECURITY - Anyone can call anytime. No code required.
//
//
// Check if API is to be remoted, and handle downlevel case if so.
//
if ( (ServerName != NULL) && ( ServerName[0] != '\0') ) {
WCHAR UncCanonServerName[UNCLEN+1];
DWORD LocalOrRemote;
NetStatus = NetpIsRemote(
(LPWSTR) ServerName, // uncanon server name
& LocalOrRemote,
UncCanonServerName, // output: canon
NIRFLAG_MAPLOCAL // flags: map null to local name
);
if (NetStatus != NERR_Success) {
goto Cleanup;
}
if (LocalOrRemote == ISREMOTE) {
//
// Do the RPC call with an exception handler since RPC will raise an
// exception if anything fails. It is up to us to figure out what
// to do once the exception is raised.
//
NET_REMOTE_TRY_RPC
//
// Call RPC version of the API.
//
*Buffer = NULL;
NetStatus = NetrGetDCName(
(LPWSTR) ServerName,
(LPWSTR) DomainName,
(LPWSTR *)Buffer );
NET_REMOTE_RPC_FAILED(
"NetGetDCName",
UncCanonServerName,
NetStatus,
NET_REMOTE_FLAG_NORMAL,
SERVICE_NETLOGON )
//
// We should probaly check if it's really a downlevel machine
//
NetStatus = RxNetGetDCName(
UncCanonServerName,
(LPWSTR) DomainName,
(LPBYTE *) Buffer // may be allocated
);
NET_REMOTE_END
goto Cleanup;
}
//
// Must be explicit reference to local machine. Fall through and
// handle it.
//
}
//
// Simply call DsGetDcName to find the PDC.
//
// NT 3.x cached the response for the primary domain. The was Lanman
// legacy to avoid the expensive discovery. However, NT discovery is
// cheaper than the API calls Lanman used to verify the cached information.
//
//
NetStatus = DsGetDcNameW(
NULL, // Not remoted
DomainName,
NULL, // No Domain GUID
NULL, // No Site GUID
DS_FORCE_REDISCOVERY |
DS_PDC_REQUIRED |
DS_IS_FLAT_NAME |
DS_RETURN_FLAT_NAME,
&DomainControllerInfo );
//
// Map the status codes to be compatible.
//
if ( NetStatus != NO_ERROR ) {
if ( NlDcUseGenericStatus(NetStatus) ) {
NetStatus = NERR_DCNotFound;
}
goto Cleanup;
}
//
// Allocate a buffer to return to the caller and fill it in
//
NetStatus = NetapipBufferAllocate(
(wcslen(DomainControllerInfo->DomainControllerName) + 1) * sizeof(WCHAR),
(LPVOID *) Buffer );
if ( NetStatus != NO_ERROR ) {
IF_DEBUG( LOGON ) {
NetpKdPrint(( "NetGetDCName: cannot allocate response buffer.\n"));
}
goto Cleanup;
}
wcscpy((LPWSTR)*Buffer, DomainControllerInfo->DomainControllerName );
Cleanup:
//
// Cleanup all locally used resources
//
if ( DomainControllerInfo != NULL ) {
NetApiBufferFree( DomainControllerInfo );
}
return NetStatus;
}
NET_API_STATUS NET_API_FUNCTION
NetGetAnyDCName (
IN LPCWSTR ServerName OPTIONAL,
IN LPCWSTR DomainName OPTIONAL,
OUT LPBYTE *Buffer
)
/*++
Routine Description:
Get the name of the any domain controller for a domain that is directly trusted
by ServerName.
If ServerName is a standalone Windows NT Workstation or standalone Windows NT Server,
no DomainName is valid.
If ServerName is a Windows NT Workstation that is a member of a domain or a
Windows NT Server member server,
the DomainName must the the domain ServerName is a member of.
If ServerName is a Windows NT Server domain controller,
the DomainName must be one of the domains trusted by the
domain the server is a controller for.
The domain controller found is guaranteed to have been up at one point during
this API call.
Arguments:
ServerName - name of remote server (null for local)
DomainName - name of domain (null for primary domain)
Buffer - Returns a pointer to an allcated buffer containing the
servername of a DC of the domain. The server name is prefixed
by \\. The buffer should be deallocated using NetApiBufferFree.
Return Value:
ERROR_SUCCESS - Success. Buffer contains DC name prefixed by \\.
ERROR_NO_LOGON_SERVERS - No DC could be found
ERROR_NO_SUCH_DOMAIN - The specified domain is not a trusted domain.
ERROR_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
broken.
ERROR_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
broken or the password is broken.
ERROR_DOMAIN_TRUST_INCONSISTENT - The server that responded is not a proper
domain controller of the specified domain.
--*/
{
NET_API_STATUS NetStatus;
//
// Do the RPC call with an exception handler since RPC will raise an
// exception if anything fails. It is up to us to figure out what
// to do once the exception is raised.
//
RpcTryExcept {
*Buffer = NULL; // Force RPC to allocate
//
// Call RPC version of the API.
//
NetStatus = NetrGetAnyDCName(
(LPWSTR) ServerName,
(LPWSTR) DomainName,
(LPWSTR *) Buffer );
} RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) ) {
NetStatus = RpcExceptionCode();
} RpcEndExcept;
IF_DEBUG( LOGON ) {
NetpKdPrint(("NetGetAnyDCName rc = %lu 0x%lx\n",
NetStatus, NetStatus));
}
return NetStatus;
}
DWORD
WINAPI
DsValidateSubnetNameA(
IN LPCSTR SubnetName
)
/*++
Routine Description:
Routine to validate a subnet name of the form xxx.xxx.xxx.xxx/yy
Arguments:
SubnetName - Name of the subnet to validate.
Return Value:
NO_ERROR: Subnet name is valid
ERROR_INVALID_NAME: Subnet name is not valid
--*/
{
DWORD WinStatus;
LPWSTR UnicodeSubnetName = NULL;
//
// Convert to unicode.
//
if ( SubnetName == NULL ) {
return ERROR_INVALID_NAME;
}
UnicodeSubnetName = NetpAllocWStrFromAStr( SubnetName );
if ( UnicodeSubnetName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// Validate the name
//
WinStatus = DsValidateSubnetNameW( UnicodeSubnetName );
//
// Clean up locally used resources.
//
Cleanup:
if ( UnicodeSubnetName != NULL ) {
NetApiBufferFree( UnicodeSubnetName );
}
return WinStatus;
}
DWORD
WINAPI
DsValidateSubnetNameW(
IN LPCWSTR SubnetName
)
/*++
Routine Description:
Routine to validate a subnet name of the form xxx.xxx.xxx.xxx/yy
Arguments:
SubnetName - Name of the subnet to validate.
Return Value:
NO_ERROR: Subnet name is valid
ERROR_INVALID_NAME: Subnet name is not valid
--*/
{
DWORD WinStatus;
ULONG SubnetAddress;
ULONG SubnetMask;
BYTE SubnetBitCount;
//
// Caller the worker routine to do the real work
//
WinStatus = NlParseSubnetString( SubnetName,
&SubnetAddress,
&SubnetMask,
&SubnetBitCount );
//
// If Winsock has not yet been initialized,
// initialize it.
//
if ( WinStatus == WSANOTINITIALISED ) {
//
// Initialize WSA.
//
WinStatus = DsWsaInitialize();
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Repeat the call.
//
WinStatus = NlParseSubnetString( SubnetName,
&SubnetAddress,
&SubnetMask,
&SubnetBitCount );
}
//
// Free locally used resouces.
//
Cleanup:
return WinStatus;
}
DWORD
WINAPI
DsGetSiteNameA(
IN LPCSTR ComputerName OPTIONAL,
OUT LPSTR *SiteName
)
/*++
Routine Description:
Same as DsGetSiteNameW except it takes and returns ASCII.
Arguments:
Same as DsGetSiteNameW except it takes and returns ASCII.
Return Value:
Same as DsGetSiteNameW except it takes and returns ASCII.
--*/
{
DWORD WinStatus;
LPWSTR UnicodeComputerName = NULL;
LPWSTR UnicodeSiteName = NULL;
//
// Convert input parameters to Unicode.
//
*SiteName = NULL;
if ( ComputerName != NULL ) {
UnicodeComputerName = NetpAllocWStrFromAStr( ComputerName );
if ( UnicodeComputerName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
//
// Call the Unicode version of the routine.
//
WinStatus = DsGetSiteNameW(
UnicodeComputerName,
&UnicodeSiteName );
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Convert the output structure to ANSI character set.
//
*SiteName = NetpAllocAStrFromWStr( UnicodeSiteName );
if ( *SiteName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
WinStatus = NO_ERROR;
//
// Clean up locally used resources.
//
Cleanup:
if ( UnicodeComputerName != NULL ) {
NetApiBufferFree( UnicodeComputerName );
}
if ( UnicodeSiteName != NULL ) {
NetApiBufferFree( UnicodeSiteName );
}
return WinStatus;
}
DWORD
WINAPI
DsGetSiteNameW(
IN LPCWSTR ComputerName OPTIONAL,
OUT LPWSTR *SiteName
)
/*++
Routine Description:
The DsGetSiteName API returns the name site a computer is in.
For a DC, the SiteName is the site the DC is configured to be in.
For a member workstation or member server, this is the name of the site
the workstation is in as configured in the domain the machine is a member of.
A standalone workstation or standalone server will always return
ERROR_NO_SITENAME.
Arguments:
ComputerName - Specifies the name of the server to remote this API to.
Typically, this parameter should be specified as NULL.
SiteName - Returns the site name of the site this machine is in.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
ERROR_NO_SITENAME - The machine is not in a site.
--*/
{
NET_API_STATUS NetStatus;
//
// Do the RPC call with an exception handler since RPC will raise an
// exception if anything fails. It is up to us to figure out what
// to do once the exception is raised.
//
RpcTryExcept {
*SiteName = NULL; // Force RPC to allocate
//
// Call RPC version of the API.
//
NetStatus = DsrGetSiteName(
(LPWSTR) ComputerName,
SiteName );
} RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) ) {
NetStatus = RpcExceptionCode();
} RpcEndExcept;
//
// If Netlogon isn't running on the local machine,
// or Netlogon is the NT 4.0 version of netlogon (the upgrade case),
// Simply indicate that there is no site.
//
if ( NetStatus == RPC_S_UNKNOWN_IF ||
NetStatus == RPC_S_PROCNUM_OUT_OF_RANGE ) {
NetStatus = ERROR_NO_SITENAME;
}
IF_DEBUG( LOGON ) {
NetpKdPrint(("DsrGetSiteName rc = %lu 0x%lx\n",
NetStatus, NetStatus));
}
return NetStatus;
}
DWORD
WINAPI
DsAddressToSiteNamesA(
IN LPCSTR ComputerName OPTIONAL,
IN DWORD EntryCount,
IN PSOCKET_ADDRESS SocketAddresses,
OUT LPSTR **SiteNames
)
/*++
Routine Description:
The DsAddressToSiteNames API returns the site names that correspond to
the specified addresses.
Arguments:
ComputerName - Specifies the name of the domain controller to remote this API to.
EntryCount - Number of addresses to convert.
SocketAddresses - Specifies an EntryCount element array of addresses
to convert. Each address must be of type AF_INET.
SiteNames - Returns an array of pointers to site names. EntryCount entries
are returned. An entry will be returned as NULL if the corresponding
address does not map to any site or if the address is malformed.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
--*/
{
NET_API_STATUS NetStatus;
PNL_SITE_NAME_ARRAY SiteNameArray;
LPWSTR UnicodeComputerName = NULL;
//
// Initialization
//
*SiteNames = NULL;
if ( EntryCount == 0 ) {
return ERROR_INVALID_PARAMETER;
}
//
// Convert input parameters to Unicode.
//
if ( ComputerName != NULL ) {
UnicodeComputerName = NetpAllocWStrFromAStr( ComputerName );
if ( UnicodeComputerName == NULL ) {
return ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Do the RPC call with an exception handler since RPC will raise an
// exception if anything fails. It is up to us to figure out what
// to do once the exception is raised.
//
RpcTryExcept {
SiteNameArray = NULL; // Force RPC to allocate
//
// Call RPC version of the API.
//
NetStatus = DsrAddressToSiteNamesW(
UnicodeComputerName,
EntryCount,
(PNL_SOCKET_ADDRESS)SocketAddresses,
&SiteNameArray );
} RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) ) {
NetStatus = RpcExceptionCode();
} RpcEndExcept;
//
// Convert the site names to what the caller expects.
//
if ( NetStatus == NO_ERROR && SiteNameArray != NULL ) {
//
// Sanity check
//
if ( EntryCount != SiteNameArray->EntryCount ) {
NetStatus = ERROR_INVALID_PARAMETER;
} else {
ULONG Size;
ULONG i;
//
// Allocate a buffer to return to the caller
//
Size = sizeof(LPSTR) * EntryCount;
for ( i=0; i<EntryCount; i++) {
Size += RtlUnicodeStringToAnsiSize( &SiteNameArray->SiteNames[i] );
}
*SiteNames = MIDL_user_allocate( Size );
if ( *SiteNames == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
} else {
LPBYTE Where;
//
// Loop copying names to the caller.
//
Where = ((LPBYTE)(*SiteNames)) + sizeof(LPSTR) * EntryCount;
for ( i=0; i<EntryCount; i++) {
//
// If no name was returned,
// pass a NULL back to the caller.
//
if ( SiteNameArray->SiteNames[i].Length == 0 ) {
(*SiteNames)[i] = NULL;
//
// Copy the site name into the return buffer.
//
} else {
ANSI_STRING TempString;
NTSTATUS Status;
(*SiteNames)[i] = (LPSTR) Where;
TempString.Buffer = Where;
TempString.MaximumLength = (USHORT) RtlUnicodeStringToAnsiSize( &SiteNameArray->SiteNames[i] );
Status = RtlUnicodeStringToAnsiString(
&TempString,
&SiteNameArray->SiteNames[i],
FALSE );
if ( !NT_SUCCESS(Status) ) {
MIDL_user_free( *SiteNames );
*SiteNames = NULL;
NetStatus = NetpNtStatusToApiStatus( Status );
break;
}
Where += TempString.MaximumLength;
}
}
}
}
MIDL_user_free( SiteNameArray );
}
IF_DEBUG( LOGON ) {
NetpKdPrint(("DsAddressToSiteNames rc = %lu 0x%lx\n",
NetStatus, NetStatus));
}
if ( UnicodeComputerName != NULL ) {
NetApiBufferFree( UnicodeComputerName );
}
return NetStatus;
}
DWORD
WINAPI
DsAddressToSiteNamesW(
IN LPCWSTR ComputerName OPTIONAL,
IN DWORD EntryCount,
IN PSOCKET_ADDRESS SocketAddresses,
OUT LPWSTR **SiteNames
)
/*++
Routine Description:
The DsAddressToSiteNames API returns the site names that correspond to
the specified addresses.
Arguments:
ComputerName - Specifies the name of the domain controller to remote this API to.
EntryCount - Number of addresses to convert.
SocketAddresses - Specifies an EntryCount element array of addresses
to convert. Each address must be of type AF_INET.
SiteNames - Returns an array of pointers to site names. EntryCount entries
are returned. An entry will be returned as NULL if the corresponding
address does not map to any site or if the address is malformed.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
--*/
{
NET_API_STATUS NetStatus;
PNL_SITE_NAME_ARRAY SiteNameArray;
//
// Initialization
//
*SiteNames = NULL;
if ( EntryCount == 0 ) {
return ERROR_INVALID_PARAMETER;
}
//
// Do the RPC call with an exception handler since RPC will raise an
// exception if anything fails. It is up to us to figure out what
// to do once the exception is raised.
//
RpcTryExcept {
SiteNameArray = NULL; // Force RPC to allocate
//
// Call RPC version of the API.
//
NetStatus = DsrAddressToSiteNamesW(
(LPWSTR) ComputerName,
EntryCount,
(PNL_SOCKET_ADDRESS)SocketAddresses,
&SiteNameArray );
} RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) ) {
NetStatus = RpcExceptionCode();
} RpcEndExcept;
//
// Convert the site names to what the caller expects.
//
if ( NetStatus == NO_ERROR && SiteNameArray != NULL ) {
//
// Sanity check
//
if ( EntryCount != SiteNameArray->EntryCount ) {
NetStatus = ERROR_INVALID_PARAMETER;
} else {
ULONG Size;
ULONG i;
//
// Allocate a buffer to return to the caller
//
Size = sizeof(LPWSTR) * EntryCount;
for ( i=0; i<EntryCount; i++) {
Size += SiteNameArray->SiteNames[i].Length + sizeof(WCHAR);
}
*SiteNames = MIDL_user_allocate( Size );
if ( *SiteNames == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
} else {
LPBYTE Where;
//
// Loop copying names to the caller.
//
Where = ((LPBYTE)(*SiteNames)) + sizeof(LPWSTR) * EntryCount;
for ( i=0; i<EntryCount; i++) {
//
// If no name was returned,
// pass a NULL back to the caller.
//
if ( SiteNameArray->SiteNames[i].Length == 0 ) {
(*SiteNames)[i] = NULL;
//
// Copy the site name into the return buffer.
//
} else {
(*SiteNames)[i] = (LPWSTR) Where;
RtlCopyMemory( Where,
SiteNameArray->SiteNames[i].Buffer,
SiteNameArray->SiteNames[i].Length );
Where += SiteNameArray->SiteNames[i].Length;
*(LPWSTR)Where = L'\0';
Where += sizeof(WCHAR);
}
}
}
}
MIDL_user_free( SiteNameArray );
}
IF_DEBUG( LOGON ) {
NetpKdPrint(("DsAddressToSiteNames rc = %lu 0x%lx\n",
NetStatus, NetStatus));
}
return NetStatus;
}
DWORD
WINAPI
DsAddressToSiteNamesExA(
IN LPCSTR ComputerName OPTIONAL,
IN DWORD EntryCount,
IN PSOCKET_ADDRESS SocketAddresses,
OUT LPSTR **SiteNames,
OUT LPSTR **SubnetNames
)
/*++
Routine Description:
The DsAddressToSiteNamesEx API returns the site names and subnet names
that correspond to the specified addresses.
Arguments:
ComputerName - Specifies the name of the domain controller to remote this API to.
EntryCount - Number of addresses to convert.
SocketAddresses - Specifies an EntryCount element array of addresses
to convert. Each address must be of type AF_INET.
SiteNames - Returns an array of pointers to site names. EntryCount entries
are returned. An entry will be returned as NULL if the corresponding
address does not map to any site or if the address is malformed.
The returned buffer must be deallocated using NetApiBufferFree.
SubnetNames - Returns an array of pointers to subnet names which were used
to perform the address to site name mappings. EntryCount entries
are returned. An entry will be returned as NULL if the corresponding address
to site mapping was not determined or if no subnet was used to perform the
corresponding address to site mapping which is the case when there is exactly
one site in the enterprise with no subnet objects mapped to it.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
--*/
{
NET_API_STATUS NetStatus;
PNL_SITE_NAME_EX_ARRAY SiteNameArray;
LPWSTR UnicodeComputerName = NULL;
//
// Initialization
//
*SiteNames = NULL;
*SubnetNames = NULL;
if ( EntryCount == 0 ) {
return ERROR_INVALID_PARAMETER;
}
//
// Convert input parameters to Unicode.
//
if ( ComputerName != NULL ) {
UnicodeComputerName = NetpAllocWStrFromAStr( ComputerName );
if ( UnicodeComputerName == NULL ) {
return ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Do the RPC call with an exception handler since RPC will raise an
// exception if anything fails. It is up to us to figure out what
// to do once the exception is raised.
//
RpcTryExcept {
SiteNameArray = NULL; // Force RPC to allocate
//
// Call RPC version of the API.
//
NetStatus = DsrAddressToSiteNamesExW(
UnicodeComputerName,
EntryCount,
(PNL_SOCKET_ADDRESS)SocketAddresses,
&SiteNameArray );
} RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) ) {
NetStatus = RpcExceptionCode();
} RpcEndExcept;
//
// Convert the site names to what the caller expects.
//
if ( NetStatus == NO_ERROR && SiteNameArray != NULL ) {
//
// Sanity check
//
if ( EntryCount != SiteNameArray->EntryCount ) {
NetStatus = ERROR_INVALID_PARAMETER;
} else {
ULONG Size;
ULONG i;
//
// Allocate a buffer to return to the caller
//
Size = sizeof(LPSTR) * EntryCount;
for ( i=0; i<EntryCount; i++) {
Size += RtlUnicodeStringToAnsiSize( &SiteNameArray->SiteNames[i] );
}
*SiteNames = MIDL_user_allocate( Size );
if ( *SiteNames == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
} else {
Size = sizeof(LPSTR) * EntryCount;
for ( i=0; i<EntryCount; i++) {
Size += RtlUnicodeStringToAnsiSize( &SiteNameArray->SubnetNames[i] );
}
*SubnetNames = MIDL_user_allocate( Size );
if ( *SubnetNames == NULL ) {
MIDL_user_free( *SiteNames );
*SiteNames = NULL;
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
} else {
LPBYTE Where;
LPBYTE Where2;
//
// Loop copying names to the caller.
//
Where = ((LPBYTE)(*SiteNames)) + sizeof(LPSTR) * EntryCount;
Where2 = ((LPBYTE)(*SubnetNames)) + sizeof(LPSTR) * EntryCount;
for ( i=0; i<EntryCount; i++) {
//
// If no name was returned,
// pass a NULL back to the caller.
//
if ( SiteNameArray->SiteNames[i].Length == 0 ) {
(*SiteNames)[i] = NULL;
//
// Copy the site name into the return buffer.
//
} else {
ANSI_STRING TempString;
NTSTATUS Status;
(*SiteNames)[i] = (LPSTR) Where;
TempString.Buffer = Where;
TempString.MaximumLength = (USHORT) RtlUnicodeStringToAnsiSize( &SiteNameArray->SiteNames[i] );
Status = RtlUnicodeStringToAnsiString(
&TempString,
&SiteNameArray->SiteNames[i],
FALSE );
if ( !NT_SUCCESS(Status) ) {
MIDL_user_free( *SiteNames );
*SiteNames = NULL;
MIDL_user_free( *SubnetNames );
*SubnetNames = NULL;
NetStatus = NetpNtStatusToApiStatus( Status );
break;
}
Where += TempString.MaximumLength;
}
//
// If no name was returned,
// pass a NULL back to the caller.
//
if ( SiteNameArray->SubnetNames[i].Length == 0 ) {
(*SubnetNames)[i] = NULL;
//
// Copy the Subnet name into the return buffer.
//
} else {
ANSI_STRING TempString;
NTSTATUS Status;
(*SubnetNames)[i] = (LPSTR) Where2;
TempString.Buffer = Where2;
TempString.MaximumLength = (USHORT) RtlUnicodeStringToAnsiSize( &SiteNameArray->SubnetNames[i] );
Status = RtlUnicodeStringToAnsiString(
&TempString,
&SiteNameArray->SubnetNames[i],
FALSE );
if ( !NT_SUCCESS(Status) ) {
MIDL_user_free( *SubnetNames );
*SubnetNames = NULL;
MIDL_user_free( *SubnetNames );
*SubnetNames = NULL;
NetStatus = NetpNtStatusToApiStatus( Status );
break;
}
Where2 += TempString.MaximumLength;
}
}
}
}
}
MIDL_user_free( SiteNameArray );
}
IF_DEBUG( LOGON ) {
NetpKdPrint(("DsAddressToSiteNames rc = %lu 0x%lx\n",
NetStatus, NetStatus));
}
return NetStatus;
}
DWORD
WINAPI
DsAddressToSiteNamesExW(
IN LPCWSTR ComputerName OPTIONAL,
IN DWORD EntryCount,
IN PSOCKET_ADDRESS SocketAddresses,
OUT LPWSTR **SiteNames,
OUT LPWSTR **SubnetNames
)
/*++
Routine Description:
The DsAddressToSiteNames API returns the site names that correspond to
the specified addresses.
Arguments:
ComputerName - Specifies the name of the domain controller to remote this API to.
EntryCount - Number of addresses to convert.
SocketAddresses - Specifies an EntryCount element array of addresses
to convert. Each address must be of type AF_INET.
SiteNames - Returns an array of pointers to site names. EntryCount entries
are returned. An entry will be returned as NULL if the corresponding
address does not map to any site or if the address is malformed.
The returned buffer must be deallocated using NetApiBufferFree.
SubnetNames - Returns an array of pointers to subnet names which were used
to perform the address to site name mappings. EntryCount entries
are returned. An entry will be returned as NULL if the corresponding address
to site mapping was not determined or if no subnet was used to perform the
corresponding address to site mapping which is the case when there is exactly
one site in the enterprise with no subnet objects mapped to it.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
--*/
{
NET_API_STATUS NetStatus;
PNL_SITE_NAME_EX_ARRAY SiteNameArray;
//
// Initialization
//
*SiteNames = NULL;
*SubnetNames = NULL;
if ( EntryCount == 0 ) {
return ERROR_INVALID_PARAMETER;
}
//
// Do the RPC call with an exception handler since RPC will raise an
// exception if anything fails. It is up to us to figure out what
// to do once the exception is raised.
//
RpcTryExcept {
SiteNameArray = NULL; // Force RPC to allocate
//
// Call RPC version of the API.
//
NetStatus = DsrAddressToSiteNamesExW(
(LPWSTR) ComputerName,
EntryCount,
(PNL_SOCKET_ADDRESS)SocketAddresses,
&SiteNameArray );
} RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) ) {
NetStatus = RpcExceptionCode();
} RpcEndExcept;
//
// Convert the site names to what the caller expects.
//
if ( NetStatus == NO_ERROR && SiteNameArray != NULL ) {
//
// Sanity check
//
if ( EntryCount != SiteNameArray->EntryCount ) {
NetStatus = ERROR_INVALID_PARAMETER;
} else {
ULONG Size;
ULONG i;
//
// Allocate a buffer to return to the caller
//
Size = sizeof(LPWSTR) * EntryCount;
for ( i=0; i<EntryCount; i++) {
Size += SiteNameArray->SiteNames[i].Length + sizeof(WCHAR);
}
*SiteNames = MIDL_user_allocate( Size );
if ( *SiteNames == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
} else {
//
// Allocate a buffer to return to the caller
//
Size = sizeof(LPWSTR) * EntryCount;
for ( i=0; i<EntryCount; i++) {
Size += SiteNameArray->SubnetNames[i].Length + sizeof(WCHAR);
}
*SubnetNames = MIDL_user_allocate( Size );
if ( *SubnetNames == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
} else {
LPBYTE Where;
LPBYTE Where2;
//
// Loop copying names to the caller.
//
Where = ((LPBYTE)(*SiteNames)) + sizeof(LPWSTR) * EntryCount;
Where2 = ((LPBYTE)(*SubnetNames)) + sizeof(LPWSTR) * EntryCount;
for ( i=0; i<EntryCount; i++) {
//
// If no name was returned,
// pass a NULL back to the caller.
//
if ( SiteNameArray->SiteNames[i].Length == 0 ) {
(*SiteNames)[i] = NULL;
//
// Copy the site name into the return buffer.
//
} else {
(*SiteNames)[i] = (LPWSTR) Where;
RtlCopyMemory( Where,
SiteNameArray->SiteNames[i].Buffer,
SiteNameArray->SiteNames[i].Length );
Where += SiteNameArray->SiteNames[i].Length;
*(LPWSTR)Where = L'\0';
Where += sizeof(WCHAR);
}
//
// If no name was returned,
// pass a NULL back to the caller.
//
if ( SiteNameArray->SubnetNames[i].Length == 0 ) {
(*SubnetNames)[i] = NULL;
//
// Copy the Subnet name into the return buffer.
//
} else {
(*SubnetNames)[i] = (LPWSTR) Where2;
RtlCopyMemory( Where2,
SiteNameArray->SubnetNames[i].Buffer,
SiteNameArray->SubnetNames[i].Length );
Where2 += SiteNameArray->SubnetNames[i].Length;
*(LPWSTR)Where2 = L'\0';
Where2 += sizeof(WCHAR);
}
}
}
}
}
MIDL_user_free( SiteNameArray );
}
IF_DEBUG( LOGON ) {
NetpKdPrint(("DsAddressToSiteNames rc = %lu 0x%lx\n",
NetStatus, NetStatus));
}
return NetStatus;
}
DWORD
WINAPI
DsGetDcSiteCoverageA(
IN LPCSTR ComputerName OPTIONAL,
OUT PULONG EntryCount,
OUT LPSTR **SiteNames
)
/*++
Routine Description:
This API returns the site names of all sites covered by a DC.
Arguments:
ComputerName - Specifies the name of the domain controller to remote this API to.
EntryCount - Returns the number of sites covered by DC.
SiteNames - Returns an array of pointers to site names.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
--*/
{
NET_API_STATUS NetStatus;
PNL_SITE_NAME_ARRAY SiteNameArray;
LPWSTR UnicodeComputerName = NULL;
//
// Initialization
//
*SiteNames = NULL;
//
// Convert input parameters to Unicode.
//
if ( ComputerName != NULL ) {
UnicodeComputerName = NetpAllocWStrFromAStr( (LPSTR)ComputerName );
if ( UnicodeComputerName == NULL ) {
return ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// Do the RPC call with an exception handler since RPC will raise an
// exception if anything fails. It is up to us to figure out what
// to do once the exception is raised.
//
RpcTryExcept {
SiteNameArray = NULL; // Force RPC to allocate
//
// Call RPC version of the API.
//
NetStatus = DsrGetDcSiteCoverageW(
UnicodeComputerName,
&SiteNameArray );
} RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) ) {
NetStatus = RpcExceptionCode();
} RpcEndExcept;
//
// Convert the site names to what the caller expects.
//
if ( NetStatus == NO_ERROR && SiteNameArray != NULL ) {
ULONG Size;
ULONG i;
//
// Set the size of the array
//
*EntryCount = SiteNameArray->EntryCount;
//
// Allocate a buffer to return to the caller
//
Size = sizeof(LPSTR) * SiteNameArray->EntryCount;
for ( i=0; i<*EntryCount; i++ ) {
Size += RtlUnicodeStringToAnsiSize( &SiteNameArray->SiteNames[i] );
}
*SiteNames = MIDL_user_allocate( Size );
if ( *SiteNames == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
} else {
LPBYTE Where;
//
// Loop copying names to the caller.
//
Where = ((LPBYTE)(*SiteNames)) + sizeof(LPSTR) * SiteNameArray->EntryCount;
for ( i=0; i<*EntryCount; i++) {
//
// Copy the site name into the return buffer.
//
ANSI_STRING TempString;
NTSTATUS Status;
(*SiteNames)[i] = (LPSTR) Where;
TempString.Buffer = Where;
TempString.MaximumLength = (USHORT) RtlUnicodeStringToAnsiSize( &SiteNameArray->SiteNames[i] );
Status = RtlUnicodeStringToAnsiString(
&TempString,
&SiteNameArray->SiteNames[i],
FALSE );
if ( !NT_SUCCESS(Status) ) {
MIDL_user_free( *SiteNames );
*SiteNames = NULL;
NetStatus = NetpNtStatusToApiStatus( Status );
break;
}
Where += TempString.MaximumLength;
}
}
MIDL_user_free( SiteNameArray );
}
IF_DEBUG( LOGON ) {
NetpKdPrint(("DsGetDcSiteCoverage rc = %lu 0x%lx\n",
NetStatus, NetStatus));
}
if ( UnicodeComputerName != NULL ) {
NetApiBufferFree( UnicodeComputerName );
}
return NetStatus;
}
DWORD
WINAPI
DsGetDcSiteCoverageW(
IN LPCWSTR ComputerName OPTIONAL,
OUT PULONG EntryCount,
OUT LPWSTR **SiteNames
)
/*++
Routine Description:
This API returns the site names of all sites covered by a DC.
Arguments:
ComputerName - Specifies the name of the domain controller to remote this API to.
EntryCount - Returns the number of sites covered by DC.
SiteNames - Returns an array of pointers to site names.
The returned buffer must be deallocated using NetApiBufferFree.
Return Value:
NO_ERROR - Operation completed successfully;
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the
operation.
--*/
{
NET_API_STATUS NetStatus;
PNL_SITE_NAME_ARRAY SiteNameArray;
//
// Initialization
//
*SiteNames = NULL;
//
// Do the RPC call with an exception handler since RPC will raise an
// exception if anything fails. It is up to us to figure out what
// to do once the exception is raised.
//
RpcTryExcept {
SiteNameArray = NULL; // Force RPC to allocate
//
// Call RPC version of the API.
//
NetStatus = DsrGetDcSiteCoverageW(
(LPWSTR) ComputerName,
&SiteNameArray );
} RpcExcept( I_RpcExceptionFilter(RpcExceptionCode()) ) {
NetStatus = RpcExceptionCode();
} RpcEndExcept;
//
// Convert the site names to what the caller expects.
//
if ( NetStatus == NO_ERROR && SiteNameArray != NULL ) {
ULONG Size;
ULONG i;
//
// Set the size of the array
//
*EntryCount = SiteNameArray->EntryCount;
//
// Allocate a buffer to return to the caller
//
Size = sizeof(LPWSTR) * SiteNameArray->EntryCount;
for ( i=0; i<*EntryCount; i++) {
Size += SiteNameArray->SiteNames[i].Length + sizeof(WCHAR);
}
*SiteNames = MIDL_user_allocate( Size );
if ( *SiteNames == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
} else {
LPBYTE Where;
//
// Loop copying names to the caller.
//
Where = ((LPBYTE)(*SiteNames)) + sizeof(LPWSTR) * SiteNameArray->EntryCount;
for ( i=0; i<*EntryCount; i++) {
//
// Copy the site name into the return buffer.
//
(*SiteNames)[i] = (LPWSTR) Where;
RtlCopyMemory( Where,
SiteNameArray->SiteNames[i].Buffer,
SiteNameArray->SiteNames[i].Length );
Where += SiteNameArray->SiteNames[i].Length;
*(LPWSTR)Where = L'\0';
Where += sizeof(WCHAR);
}
}
MIDL_user_free( SiteNameArray );
}
IF_DEBUG( LOGON ) {
NetpKdPrint(("DsGetDcSiteCoverage rc = %lu 0x%lx\n",
NetStatus, NetStatus));
}
return NetStatus;
}
#endif // WIN32_CHICAGO
#endif // NETTEST_UTILITY