1099 lines
28 KiB
C
1099 lines
28 KiB
C
/*++
|
||
|
||
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;
|
||
}
|