windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/srvsvc/client/dfsstub.c

1099 lines
28 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}