You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1098 lines
29 KiB
1098 lines
29 KiB
/*++
|
|
|
|
Copyright (c) 1991-1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dfsstub.c
|
|
|
|
Abstract:
|
|
|
|
These are the server service API RPC client stubs for DFS operations
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
--*/
|
|
|
|
//
|
|
// INCLUDES
|
|
//
|
|
|
|
#include <nt.h> // DbgPrint prototype
|
|
|
|
#include <ntrtl.h> // DbgPrint
|
|
#include <rpc.h> // DataTypes and runtime APIs
|
|
|
|
#include <srvsvc.h> // generated by the MIDL complier
|
|
#include <lmcons.h> // NET_API_STATUS
|
|
#include <debuglib.h> // (needed by netrpc.h)
|
|
#include <lmsvc.h> // (needed by netrpc.h)
|
|
#include <netdebug.h> // (needed by netrpc.h)
|
|
#include <lmerr.h> // NetError codes
|
|
#include <netrpc.h> // NET_REMOTE_ macros.
|
|
#include <nturtl.h>
|
|
#include <winbase.h>
|
|
#include <dfspriv.h>
|
|
#include <Winsock2.h>
|
|
#include <Dsgetdc.h>
|
|
#include <malloc.h>
|
|
#include <stdio.h>
|
|
#include <Lm.h>
|
|
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsGetVersion(
|
|
IN LPWSTR servername,
|
|
OUT LPDWORD Version)
|
|
{
|
|
NET_API_STATUS apiStatus;
|
|
|
|
NET_REMOTE_TRY_RPC
|
|
|
|
apiStatus = NetrDfsGetVersion( servername, Version );
|
|
|
|
NET_REMOTE_RPC_FAILED(
|
|
"I_NetDfsGetVersion",
|
|
servername,
|
|
apiStatus,
|
|
NET_REMOTE_FLAG_NORMAL,
|
|
SERVICE_SERVER)
|
|
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
|
|
NET_REMOTE_END
|
|
|
|
return(apiStatus);
|
|
|
|
}
|
|
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsCreateLocalPartition (
|
|
IN LPWSTR servername,
|
|
IN LPWSTR ShareName,
|
|
IN LPGUID EntryUid,
|
|
IN LPWSTR EntryPrefix,
|
|
IN LPWSTR ShortName,
|
|
IN LPNET_DFS_ENTRY_ID_CONTAINER RelationInfo,
|
|
IN BOOL Force
|
|
)
|
|
{
|
|
NET_API_STATUS apiStatus;
|
|
|
|
NET_REMOTE_TRY_RPC
|
|
|
|
apiStatus = NetrDfsCreateLocalPartition (
|
|
servername,
|
|
ShareName,
|
|
EntryUid,
|
|
EntryPrefix,
|
|
ShortName,
|
|
RelationInfo,
|
|
Force
|
|
);
|
|
|
|
NET_REMOTE_RPC_FAILED(
|
|
"NetDfsCreateLocalPartition",
|
|
servername,
|
|
apiStatus,
|
|
NET_REMOTE_FLAG_NORMAL,
|
|
SERVICE_SERVER)
|
|
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
|
|
NET_REMOTE_END
|
|
|
|
return(apiStatus);
|
|
|
|
}
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsDeleteLocalPartition (
|
|
IN LPWSTR servername OPTIONAL,
|
|
IN LPGUID Uid,
|
|
IN LPWSTR Prefix
|
|
)
|
|
{
|
|
NET_API_STATUS apiStatus;
|
|
|
|
NET_REMOTE_TRY_RPC
|
|
|
|
apiStatus = NetrDfsDeleteLocalPartition (
|
|
servername,
|
|
Uid,
|
|
Prefix
|
|
);
|
|
|
|
NET_REMOTE_RPC_FAILED(
|
|
"NetDfsDeleteLocalPartition",
|
|
servername,
|
|
apiStatus,
|
|
NET_REMOTE_FLAG_NORMAL,
|
|
SERVICE_SERVER)
|
|
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
|
|
NET_REMOTE_END;
|
|
|
|
return apiStatus;
|
|
}
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsSetLocalVolumeState (
|
|
IN LPWSTR servername OPTIONAL,
|
|
IN LPGUID Uid,
|
|
IN LPWSTR Prefix,
|
|
IN ULONG State
|
|
)
|
|
{
|
|
NET_API_STATUS apiStatus;
|
|
|
|
NET_REMOTE_TRY_RPC
|
|
|
|
apiStatus = NetrDfsSetLocalVolumeState (
|
|
servername,
|
|
Uid,
|
|
Prefix,
|
|
State
|
|
);
|
|
|
|
NET_REMOTE_RPC_FAILED(
|
|
"NetDfsSetLocalVolumeState",
|
|
servername,
|
|
apiStatus,
|
|
NET_REMOTE_FLAG_NORMAL,
|
|
SERVICE_SERVER)
|
|
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
|
|
NET_REMOTE_END;
|
|
|
|
return apiStatus;
|
|
}
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsSetServerInfo (
|
|
IN LPWSTR servername OPTIONAL,
|
|
IN LPGUID Uid,
|
|
IN LPWSTR Prefix
|
|
)
|
|
{
|
|
NET_API_STATUS apiStatus;
|
|
|
|
NET_REMOTE_TRY_RPC
|
|
|
|
apiStatus = NetrDfsSetServerInfo (
|
|
servername,
|
|
Uid,
|
|
Prefix
|
|
);
|
|
|
|
NET_REMOTE_RPC_FAILED(
|
|
"NetDfsSetServerInfo",
|
|
servername,
|
|
apiStatus,
|
|
NET_REMOTE_FLAG_NORMAL,
|
|
SERVICE_SERVER)
|
|
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
|
|
NET_REMOTE_END;
|
|
|
|
return apiStatus;
|
|
}
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsCreateExitPoint (
|
|
IN LPWSTR servername OPTIONAL,
|
|
IN LPGUID Uid,
|
|
IN LPWSTR Prefix,
|
|
IN ULONG Type,
|
|
IN ULONG ShortPrefixSize,
|
|
OUT LPWSTR ShortPrefix
|
|
)
|
|
{
|
|
NET_API_STATUS apiStatus;
|
|
|
|
NET_REMOTE_TRY_RPC
|
|
|
|
apiStatus = NetrDfsCreateExitPoint (
|
|
servername,
|
|
Uid,
|
|
Prefix,
|
|
Type,
|
|
ShortPrefixSize,
|
|
ShortPrefix
|
|
);
|
|
|
|
NET_REMOTE_RPC_FAILED(
|
|
"NetDfsCreateExitPoint",
|
|
servername,
|
|
apiStatus,
|
|
NET_REMOTE_FLAG_NORMAL,
|
|
SERVICE_SERVER)
|
|
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
|
|
NET_REMOTE_END;
|
|
|
|
return apiStatus;
|
|
}
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsDeleteExitPoint (
|
|
IN LPWSTR servername OPTIONAL,
|
|
IN LPGUID Uid,
|
|
IN LPWSTR Prefix,
|
|
IN ULONG Type
|
|
)
|
|
{
|
|
NET_API_STATUS apiStatus;
|
|
|
|
NET_REMOTE_TRY_RPC
|
|
|
|
apiStatus = NetrDfsDeleteExitPoint (
|
|
servername,
|
|
Uid,
|
|
Prefix,
|
|
Type
|
|
);
|
|
|
|
NET_REMOTE_RPC_FAILED(
|
|
"NetDfsDeleteExitPoint",
|
|
servername,
|
|
apiStatus,
|
|
NET_REMOTE_FLAG_NORMAL,
|
|
SERVICE_SERVER)
|
|
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
|
|
NET_REMOTE_END;
|
|
|
|
return apiStatus;
|
|
}
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsModifyPrefix (
|
|
IN LPWSTR servername OPTIONAL,
|
|
IN LPGUID Uid,
|
|
IN LPWSTR Prefix
|
|
)
|
|
{
|
|
NET_API_STATUS apiStatus;
|
|
|
|
NET_REMOTE_TRY_RPC
|
|
|
|
apiStatus = NetrDfsModifyPrefix (
|
|
servername,
|
|
Uid,
|
|
Prefix
|
|
);
|
|
|
|
NET_REMOTE_RPC_FAILED(
|
|
"NetDfsModifyPrefix",
|
|
servername,
|
|
apiStatus,
|
|
NET_REMOTE_FLAG_NORMAL,
|
|
SERVICE_SERVER)
|
|
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
|
|
NET_REMOTE_END;
|
|
|
|
return apiStatus;
|
|
}
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsFixLocalVolume (
|
|
IN LPWSTR servername OPTIONAL,
|
|
IN LPWSTR VolumeName,
|
|
IN ULONG EntryType,
|
|
IN ULONG ServiceType,
|
|
IN LPWSTR StgId,
|
|
IN LPGUID EntryUid, // unique id for this partition
|
|
IN LPWSTR EntryPrefix, // path prefix for this partition
|
|
IN LPNET_DFS_ENTRY_ID_CONTAINER RelationInfo,
|
|
IN ULONG CreateDisposition
|
|
)
|
|
{
|
|
NET_API_STATUS apiStatus;
|
|
|
|
NET_REMOTE_TRY_RPC
|
|
|
|
apiStatus = NetrDfsFixLocalVolume (
|
|
servername,
|
|
VolumeName,
|
|
EntryType,
|
|
ServiceType,
|
|
StgId,
|
|
EntryUid,
|
|
EntryPrefix,
|
|
RelationInfo,
|
|
CreateDisposition
|
|
);
|
|
|
|
NET_REMOTE_RPC_FAILED(
|
|
"NetDfsFixLocalVolume",
|
|
servername,
|
|
apiStatus,
|
|
NET_REMOTE_FLAG_NORMAL,
|
|
SERVICE_SERVER)
|
|
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
|
|
NET_REMOTE_END;
|
|
|
|
return apiStatus;
|
|
}
|
|
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsManagerReportSiteInfo (
|
|
IN LPWSTR ServerName,
|
|
OUT LPDFS_SITELIST_INFO *ppSiteInfo
|
|
)
|
|
{
|
|
struct sockaddr_in Destination;
|
|
struct hostent * pHostEnt;
|
|
SOCKET_ADDRESS SocketAddress;
|
|
NET_API_STATUS apiStatus;
|
|
LPWSTR *SiteName = NULL;
|
|
char* ServerNameA = NULL;
|
|
|
|
NET_REMOTE_TRY_RPC
|
|
|
|
apiStatus = NetrDfsManagerReportSiteInfo (
|
|
ServerName,
|
|
ppSiteInfo
|
|
);
|
|
|
|
|
|
|
|
NET_REMOTE_RPC_FAILED(
|
|
"NetDfsMangerReportSiteInfo",
|
|
ServerName,
|
|
apiStatus,
|
|
NET_REMOTE_FLAG_NORMAL,
|
|
SERVICE_SERVER)
|
|
|
|
|
|
NET_REMOTE_END;
|
|
|
|
|
|
if(apiStatus != ERROR_SUCCESS) {
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
int err;
|
|
PDOMAIN_CONTROLLER_INFO pDCInfo;
|
|
|
|
wVersionRequested = MAKEWORD( 2, 2 );
|
|
|
|
err = WSAStartup( wVersionRequested, &wsaData );
|
|
if ( err != 0 ) {
|
|
/* We could not find a usable */
|
|
/* WinSock DLL. */
|
|
return apiStatus;
|
|
}
|
|
|
|
|
|
|
|
// we couldn't get the site name
|
|
ServerNameA = malloc(wcslen(ServerName) + 1);
|
|
|
|
if(ServerNameA == NULL) {
|
|
apiStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
} else {
|
|
// need to convert from WCHAR* to char*
|
|
sprintf(ServerNameA, "%ws", ServerName);
|
|
|
|
if ((pHostEnt = gethostbyname(ServerNameA)) != NULL) {
|
|
memcpy(&(Destination.sin_addr), pHostEnt->h_addr, pHostEnt->h_length);
|
|
Destination.sin_family = pHostEnt->h_addrtype;
|
|
|
|
if(pHostEnt->h_addrtype != AF_INET) {
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
} else {
|
|
SocketAddress.lpSockaddr = (struct sockaddr *)&Destination;
|
|
SocketAddress.iSockaddrLength = sizeof(Destination);
|
|
Destination.sin_port = 0;
|
|
|
|
dwErr = DsGetDcName(
|
|
NULL, // Computer to remote to
|
|
NULL, // Domain - use local domain
|
|
NULL, // Domain Guid
|
|
NULL, // Site Guid
|
|
0, // Flags
|
|
&pDCInfo);
|
|
|
|
if(dwErr == ERROR_SUCCESS) {
|
|
apiStatus = DsAddressToSiteNames(pDCInfo->DomainControllerAddress,
|
|
1,
|
|
&SocketAddress,
|
|
&SiteName
|
|
);
|
|
|
|
NetApiBufferFree( pDCInfo );
|
|
} else {
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if(apiStatus == NO_ERROR) {
|
|
if((SiteName == NULL) || (*SiteName == NULL)) {
|
|
// If DsAddressToSiteNames can't map to a site name,
|
|
// it returns success but sets the buffer to NULL.
|
|
apiStatus = ERROR_NO_SITENAME;
|
|
} else {
|
|
// we got the site name
|
|
apiStatus = NetApiBufferAllocate(
|
|
sizeof(DFS_SITELIST_INFO) + ((wcslen(*SiteName) + 1) * sizeof(WCHAR)),
|
|
ppSiteInfo
|
|
);
|
|
|
|
if(apiStatus == ERROR_SUCCESS) {
|
|
(*ppSiteInfo)->cSites = 1;
|
|
(*ppSiteInfo)->Site[0].SiteName = (LPWSTR)((ULONG_PTR)(*ppSiteInfo) + sizeof(DFS_SITELIST_INFO));
|
|
wcscpy((*ppSiteInfo)->Site[0].SiteName, *SiteName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
apiStatus = WSAGetLastError();
|
|
apiStatus = ERROR_NOT_SUPPORTED;
|
|
}
|
|
free(ServerNameA);
|
|
|
|
}
|
|
|
|
WSACleanup();
|
|
}
|
|
|
|
|
|
return apiStatus;
|
|
}
|
|
|
|
|
|
|
|
#include <dsgetdc.h>
|
|
#include <winldap.h>
|
|
#include <lmapibuf.h>
|
|
|
|
//
|
|
// This is the container which holds the DFS configuration data
|
|
//
|
|
static const WCHAR DfsConfigContainer[] = L"CN=Dfs-Configuration,CN=System";
|
|
|
|
typedef struct
|
|
{
|
|
int cPieces;
|
|
PCHAR rpPieces[1];
|
|
} DNS_NAME, *PDNS_NAME;
|
|
|
|
static
|
|
DWORD
|
|
BreakDnsName(
|
|
IN CHAR *pName,
|
|
OUT PDNS_NAME *ppDnsName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Breaks a DNS name in dotted string format (eg: dbsd.microsoft.com) into
|
|
its constituent parts.
|
|
|
|
Arguments:
|
|
|
|
pName - pointer to string representing dotted DNS name to break.
|
|
|
|
ppDnsName - pointer to pointer to DNS_NAME struct which should be
|
|
deallocated by NetApiBufferFree().
|
|
--*/
|
|
|
|
{
|
|
int cPieces;
|
|
CHAR *p;
|
|
DWORD cBytes;
|
|
CHAR *buffer;
|
|
int i;
|
|
LPSTR seps = ".";
|
|
|
|
if ( (NULL == pName) || ('\0' == *pName) || ('.' == *pName) )
|
|
{
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
// Count number of pieces so we can figure out how much to allocate.
|
|
|
|
cPieces = 1;
|
|
p = pName;
|
|
|
|
for ( p = pName; '\0' != *p; p++ )
|
|
{
|
|
if ( '.' == *p )
|
|
{
|
|
cPieces++;
|
|
}
|
|
}
|
|
|
|
// Calculate bytes to allocate. Allocate memory which will hold (in order)
|
|
// the DNS_NAME struct, the DNS_NAME.rpPieces pointer array, and finally
|
|
// a scratch buffer where we can strtok the input name.
|
|
|
|
cBytes = sizeof(DNS_NAME);
|
|
cBytes += cPieces * sizeof(PCHAR);
|
|
cBytes += strlen(pName) + 1;
|
|
|
|
NetApiBufferAllocate( cBytes, (PVOID *)ppDnsName );
|
|
|
|
if ( *ppDnsName == NULL )
|
|
{
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
// Fill in the buffer and call strtok as often as required to chop it
|
|
// into pieces filling the DNS_NAME as we go.
|
|
|
|
buffer = (CHAR *) &((*ppDnsName)->rpPieces[cPieces]);
|
|
strcpy(buffer, pName);
|
|
|
|
(*ppDnsName)->cPieces = cPieces;
|
|
(*ppDnsName)->rpPieces[0] = strtok(buffer, seps);
|
|
|
|
for ( i = 1; i < cPieces; i++ )
|
|
{
|
|
(*ppDnsName)->rpPieces[i] = strtok(NULL, seps);
|
|
}
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
static
|
|
DWORD
|
|
FindContext(
|
|
IN CHAR *pName,
|
|
IN int cDnValues,
|
|
IN CHAR **rpDnValues,
|
|
OUT int *pMatchingValueIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines the best match of a DNS name (eg: dbsd.microsoft.com)
|
|
to a set of RFC 1779 DNs (eg: ou=dbsd, ou=microsoft, c=us). We assume
|
|
that the array of DNs represent NT5 DS naming contexts (i.e. domains)
|
|
which is true with the exception of the Configuration naming context.
|
|
For example, let's say a DC hosted three naming contexts:
|
|
|
|
1 - ou=dbsd, ou=microsoft, c=us
|
|
2 - ou=nt, ou=dbsd, ou=microsoft, c=us
|
|
3 - ou=configuration, ou=microsoft, c=us
|
|
|
|
Then dbsd.microsoft.com would match the 1st DN in the list. This is not
|
|
foolproof in the case of a deviant namespace which has a domain structure
|
|
like:
|
|
|
|
ou=dbsd, ou=microsoft, ou=com, ou=dbsd, ou=microsoft, c=us
|
|
|
|
But anyone with a namespace like that is going to have other problems
|
|
anyway.
|
|
|
|
Arguments:
|
|
|
|
pName - pointer to DNS name to match.
|
|
|
|
cDnValues - count of values in rpDnValues.
|
|
|
|
rpDnValues - array of pointers to DNs to match against.
|
|
|
|
pMatchingValueIndex - pointer to int which will identify the best
|
|
matching DN in rpDnValues on successful return.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - success
|
|
ERROR_NOT_ENOUGH_MEMORY - allocation error
|
|
ERROR_INVALID_PARAMETER - invalid parameter
|
|
ERROR_INVALID_DOMAINNAME - bad DNS or DN domain name
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr;
|
|
int i, j;
|
|
CHAR **rpDn = NULL;
|
|
int currentMatchLength;
|
|
int bestMatchLength;
|
|
int bestMatchIndex;
|
|
PDNS_NAME pDomainDnsName = NULL;
|
|
|
|
dwErr = BreakDnsName(pName, &pDomainDnsName);
|
|
|
|
if ( NO_ERROR != dwErr )
|
|
{
|
|
return(dwErr);
|
|
}
|
|
|
|
// Iterate over the DN values and see which one has the longest match.
|
|
|
|
bestMatchIndex = 0;
|
|
bestMatchLength = -1;
|
|
|
|
for ( i = 0; i < cDnValues; i++ )
|
|
{
|
|
rpDn = ldap_explode_dn(rpDnValues[i], 1); // 1 ==> notypes
|
|
|
|
if ( NULL == rpDn )
|
|
{
|
|
dwErr = ERROR_INVALID_DOMAINNAME;
|
|
goto Cleanup;
|
|
}
|
|
|
|
currentMatchLength = 0;
|
|
|
|
// Try to match each piece of the domain name to each piece of the
|
|
// DN. Fortunately, RFC 1779 DNs are ordered least to most significant
|
|
// just as DNS domain names are. rpDn[] is "terminated" with a NULL.
|
|
|
|
for ( j = 0; (j < pDomainDnsName->cPieces) && (NULL != rpDn[j]); j++ )
|
|
{
|
|
if ( 0 == _stricmp(pDomainDnsName->rpPieces[j], rpDn[j]) )
|
|
{
|
|
currentMatchLength++;
|
|
}
|
|
}
|
|
|
|
if ( (0 != currentMatchLength) &&
|
|
(currentMatchLength > bestMatchLength) )
|
|
{
|
|
bestMatchLength = currentMatchLength;
|
|
bestMatchIndex = i;
|
|
}
|
|
|
|
ldap_value_free(rpDn);
|
|
}
|
|
|
|
*pMatchingValueIndex = bestMatchIndex;
|
|
dwErr = NO_ERROR;
|
|
|
|
Cleanup:
|
|
|
|
if ( pDomainDnsName != NULL ) {
|
|
NetApiBufferFree( pDomainDnsName );
|
|
}
|
|
|
|
return(dwErr);
|
|
}
|
|
|
|
/*
|
|
* This API returns a vector of \\server\share combinations which form the
|
|
* root of a Fault Tolerant DFS. This null-terminated vector should be
|
|
* freed by the caller with NetApiBufferFree().
|
|
*
|
|
* If pLDAP is supplied, we asssume that this is the handle to the DS server
|
|
* holding the configuration data. Else, we use wszDomainName to locate the
|
|
* proper DS server.
|
|
*
|
|
* wszDfsName is the name of the fault tolerant DFS for which individual servers
|
|
* are to be discovered.
|
|
*
|
|
*/
|
|
NET_API_STATUS NET_API_FUNCTION
|
|
I_NetDfsGetFtServers(
|
|
IN PVOID LdapInputArg OPTIONAL,
|
|
IN LPWSTR wszDomainName OPTIONAL,
|
|
IN LPWSTR wszDfsName OPTIONAL,
|
|
OUT LPWSTR **List
|
|
)
|
|
{
|
|
PLDAP pLDAP = (PLDAP)LdapInputArg;
|
|
BOOLEAN bUnbindNeeded = FALSE;
|
|
DWORD dwErr;
|
|
NTSTATUS status;
|
|
PWCHAR attrs[2];
|
|
LDAPMessage *pMsg = NULL;
|
|
LDAPMessage *pEntry = NULL;
|
|
WCHAR *pAttr = NULL;
|
|
WCHAR **rpValues = NULL;
|
|
WCHAR **allValues = NULL;
|
|
WCHAR ***rpValuesToFree = NULL;
|
|
INT cValues = 0;
|
|
INT i;
|
|
WCHAR *dfsDn = NULL;
|
|
DWORD len;
|
|
USHORT cChar;
|
|
PWCHAR *resultVector;
|
|
ULONG cBytes;
|
|
|
|
if (List == NULL) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*List = NULL;
|
|
|
|
if (!ARGUMENT_PRESENT(pLDAP)) {
|
|
|
|
DOMAIN_CONTROLLER_INFO *pInfo = NULL;
|
|
ULONG dsAdditionalFlags = 0;
|
|
ULONG retry;
|
|
|
|
for (retry = 0; pLDAP == NULL && retry < 2; retry++) {
|
|
|
|
//
|
|
// Find a DC for the given domain.
|
|
//
|
|
dwErr = DsGetDcName(
|
|
NULL, // computer name
|
|
wszDomainName, // DNS domain name
|
|
NULL, // domain guid
|
|
NULL, // site guid
|
|
DS_DIRECTORY_SERVICE_REQUIRED |
|
|
DS_IP_REQUIRED |
|
|
dsAdditionalFlags,
|
|
&pInfo);
|
|
|
|
if (dwErr != NO_ERROR) {
|
|
return dwErr;
|
|
}
|
|
|
|
//
|
|
// DomainControllerAddress is prefixed with "\\" so
|
|
// aditionally ensure there's some useful data there.
|
|
//
|
|
|
|
if (DS_INET_ADDRESS != pInfo->DomainControllerAddressType ||
|
|
(cChar = (USHORT)wcslen(pInfo->DomainControllerAddress)) < 3) {
|
|
|
|
NetApiBufferFree(pInfo);
|
|
return ERROR_NO_SUCH_DOMAIN;
|
|
}
|
|
|
|
//
|
|
// Try to connect to the DS server on the DC
|
|
//
|
|
|
|
pLDAP = ldap_openW(&pInfo->DomainControllerAddress[2], 0);
|
|
|
|
if (pLDAP == NULL) {
|
|
//
|
|
// Couldn't connect. Let's force rediscovery and see if we
|
|
// can connect to a DC which is working!
|
|
//
|
|
NetApiBufferFree(pInfo);
|
|
dsAdditionalFlags |= DS_FORCE_REDISCOVERY;
|
|
|
|
} else {
|
|
|
|
dwErr = ldap_bind_s(pLDAP, NULL, NULL, LDAP_AUTH_SSPI);
|
|
|
|
}
|
|
|
|
NetApiBufferFree(pInfo);
|
|
|
|
}
|
|
|
|
if (pLDAP == NULL || dwErr != LDAP_SUCCESS) {
|
|
return ERROR_PATH_NOT_FOUND;
|
|
}
|
|
|
|
bUnbindNeeded = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// Read the namingContexts operational attribute.
|
|
//
|
|
|
|
pLDAP->ld_sizelimit = 0; // no search limit
|
|
pLDAP->ld_timelimit = 0; // no time limit
|
|
pLDAP->ld_deref = LDAP_DEREF_NEVER;
|
|
|
|
attrs[0] = L"defaultnamingContext";
|
|
attrs[1] = NULL;
|
|
|
|
if ((dwErr = ldap_search_sW(
|
|
pLDAP,
|
|
L"", // search base
|
|
LDAP_SCOPE_BASE,
|
|
L"(objectClass=*)", // filter
|
|
attrs,
|
|
0, // attrs and values
|
|
&pMsg)) != LDAP_SUCCESS) {
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure we got back something reasonable
|
|
//
|
|
if (ldap_count_entries(pLDAP, pMsg) != 1 ||
|
|
(pEntry = ldap_first_entry(pLDAP, pMsg)) == NULL ||
|
|
(rpValues = ldap_get_valuesW(pLDAP, pEntry, attrs[0])) == NULL ||
|
|
(cValues = ldap_count_valuesW(rpValues)) == 0
|
|
) {
|
|
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(wszDfsName)) {
|
|
|
|
//
|
|
// Looks good. Allocate enough memory to hold the DN of the
|
|
// DFS configuration data for the fault tolerant DFS in question
|
|
//
|
|
|
|
len = (DWORD)(3 * sizeof(WCHAR) +
|
|
(wcslen(wszDfsName) + 1) * sizeof(WCHAR) +
|
|
(wcslen(DfsConfigContainer) + 1) * sizeof(WCHAR) +
|
|
(wcslen(rpValues[0]) + 1) * sizeof(WCHAR));
|
|
|
|
dwErr = NetApiBufferAllocate(len, (PVOID *)&dfsDn);
|
|
|
|
if (dfsDn == NULL) {
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Construct the DN
|
|
//
|
|
|
|
RtlZeroMemory(dfsDn, len);
|
|
wcscpy(dfsDn, L"CN=");
|
|
wcscat(dfsDn, wszDfsName);
|
|
wcscat(dfsDn, L",");
|
|
wcscat(dfsDn, DfsConfigContainer);
|
|
wcscat(dfsDn, L",");
|
|
wcscat(dfsDn, rpValues[0]);
|
|
|
|
//
|
|
// Now see if we can get at the 'remoteServerName' property of this object.
|
|
// This property holds the names of the servers hosting this DFS
|
|
//
|
|
|
|
pLDAP->ld_sizelimit = 0;
|
|
pLDAP->ld_timelimit= 0;
|
|
pLDAP->ld_deref = LDAP_DEREF_NEVER;
|
|
|
|
ldap_msgfree(pMsg);
|
|
pMsg = NULL;
|
|
|
|
ldap_value_freeW(rpValues);
|
|
rpValues = NULL;
|
|
|
|
attrs[0] = L"remoteServerName";
|
|
attrs[1] = NULL;
|
|
|
|
dwErr = ldap_search_sW(
|
|
pLDAP,
|
|
dfsDn,
|
|
LDAP_SCOPE_BASE,
|
|
L"(objectClass=*)",
|
|
attrs,
|
|
0,
|
|
&pMsg);
|
|
|
|
//
|
|
// Make sure the result is reasonable
|
|
//
|
|
if (ldap_count_entries(pLDAP, pMsg) == 0 ||
|
|
(pEntry = ldap_first_entry(pLDAP, pMsg)) == NULL ||
|
|
(rpValues = ldap_get_valuesW(pLDAP, pEntry, attrs[0])) == NULL ||
|
|
rpValues[0][0] == L'\0'
|
|
) {
|
|
|
|
dwErr = ERROR_PATH_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// The result is reasonable, just point allValues to rpValues
|
|
//
|
|
|
|
allValues = rpValues;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The caller is trying to retrieve the names of all the FT DFSs in the domain
|
|
//
|
|
// Allocate enough memory to hold the DN of the
|
|
// DFS configuration container
|
|
//
|
|
|
|
len = (wcslen(DfsConfigContainer) + 1) * sizeof(WCHAR) +
|
|
(wcslen(rpValues[0]) + 1) * sizeof(WCHAR);
|
|
|
|
dwErr = NetApiBufferAllocate(len, (PVOID *)&dfsDn);
|
|
|
|
if (dfsDn == NULL) {
|
|
dwErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Construct the DN
|
|
//
|
|
|
|
RtlZeroMemory(dfsDn, len);
|
|
wcscpy(dfsDn, DfsConfigContainer);
|
|
wcscat(dfsDn, L",");
|
|
wcscat(dfsDn, rpValues[0]);
|
|
|
|
//
|
|
// Now see if we can enumerate the objects below this one. The names
|
|
// of these objects will be the different FT dfs's available
|
|
//
|
|
pLDAP->ld_sizelimit = 0;
|
|
pLDAP->ld_timelimit= 0;
|
|
pLDAP->ld_deref = LDAP_DEREF_NEVER;
|
|
|
|
ldap_msgfree(pMsg);
|
|
pMsg = NULL;
|
|
|
|
ldap_value_freeW(rpValues);
|
|
rpValues = NULL;
|
|
|
|
attrs[0] = L"CN";
|
|
attrs[1] = NULL;
|
|
|
|
dwErr = ldap_search_sW(
|
|
pLDAP,
|
|
dfsDn,
|
|
LDAP_SCOPE_ONELEVEL,
|
|
L"(objectClass=fTDfs)",
|
|
attrs,
|
|
0,
|
|
&pMsg);
|
|
|
|
//
|
|
// Make sure the result is reasonable
|
|
//
|
|
if (
|
|
((cValues = ldap_count_entries(pLDAP, pMsg)) == 0) ||
|
|
(pEntry = ldap_first_entry(pLDAP, pMsg)) == NULL
|
|
) {
|
|
dwErr = ERROR_PATH_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// The search for all FTDfs's returns multiple entries, each with
|
|
// one value for the object's CN. Coalesce these into a single array.
|
|
//
|
|
|
|
dwErr = NetApiBufferAllocate(2 * (cValues + 1) * sizeof(PWSTR), (PVOID *)&allValues);
|
|
|
|
if (dwErr != ERROR_SUCCESS) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
rpValuesToFree = (WCHAR ***) &allValues[cValues + 1];
|
|
|
|
for (i = 0; (i < cValues) && (dwErr == ERROR_SUCCESS); i++) {
|
|
|
|
rpValues = ldap_get_valuesW(pLDAP, pEntry, attrs[0]);
|
|
rpValuesToFree[i] = rpValues;
|
|
//
|
|
// Sanity check
|
|
//
|
|
if (ldap_count_valuesW(rpValues) == 0 || rpValues[0][0] == L'\0') {
|
|
dwErr = ERROR_PATH_NOT_FOUND;
|
|
} else {
|
|
allValues[i] = rpValues[0];
|
|
pEntry = ldap_next_entry(pLDAP, pEntry);
|
|
}
|
|
|
|
}
|
|
|
|
if (dwErr == ERROR_SUCCESS) {
|
|
allValues[i] = NULL;
|
|
rpValuesToFree[i] = NULL;
|
|
} else {
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
if (dwErr != LDAP_SUCCESS) {
|
|
dwErr = ERROR_PATH_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Now we need to allocate the memory to hold this vector and return the results.
|
|
//
|
|
// First see how much space we need
|
|
//
|
|
|
|
for (len = cValues = 0; allValues[cValues]; cValues++) {
|
|
len += sizeof(LPWSTR) + (wcslen(allValues[cValues]) + 1) * sizeof(WCHAR);
|
|
}
|
|
len += sizeof(LPWSTR); // for the final NULL pointer
|
|
|
|
dwErr = NetApiBufferAllocate(len, (PVOID *)&resultVector);
|
|
|
|
if (dwErr == NO_ERROR) {
|
|
|
|
LPWSTR pstr = (LPWSTR)((PCHAR)resultVector + (cValues + 1) * sizeof(LPWSTR));
|
|
ULONG slen;
|
|
|
|
RtlZeroMemory(resultVector, len);
|
|
|
|
len -= (cValues+1) * sizeof(LPWSTR);
|
|
|
|
for (cValues = 0; allValues[cValues] && len >= sizeof(WCHAR); cValues++) {
|
|
|
|
resultVector[cValues] = pstr;
|
|
wcscpy(pstr, allValues[cValues]);
|
|
slen = wcslen(allValues[cValues]);
|
|
pstr += slen + 1;
|
|
len -= (slen + 1) * sizeof(WCHAR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dwErr == NO_ERROR) {
|
|
*List = resultVector;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (ARGUMENT_PRESENT(wszDfsName)) {
|
|
if (rpValues != NULL) {
|
|
ldap_value_freeW(rpValues);
|
|
}
|
|
} else {
|
|
if (rpValuesToFree != NULL) {
|
|
for (i = 0; rpValuesToFree[i] != NULL; i++) {
|
|
ldap_value_freeW(rpValuesToFree[i]);
|
|
}
|
|
}
|
|
if (allValues != NULL) {
|
|
NetApiBufferFree(allValues);
|
|
}
|
|
}
|
|
|
|
if (pMsg != NULL) {
|
|
ldap_msgfree(pMsg);
|
|
}
|
|
|
|
if (dfsDn != NULL) {
|
|
NetApiBufferFree(dfsDn);
|
|
}
|
|
|
|
if (pLDAP != NULL && bUnbindNeeded == TRUE) {
|
|
ldap_unbind(pLDAP);
|
|
}
|
|
|
|
return dwErr;
|
|
}
|