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.
4780 lines
156 KiB
4780 lines
156 KiB
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <winldap.h>
|
|
#include <ntldap.h>
|
|
#include <schedule.h>
|
|
#include <mkdso.h>
|
|
#include <frsrpc.h>
|
|
#include <ntdsapi.h>
|
|
#include <winber.h>
|
|
|
|
|
|
#define ArgMatch(_a, _b) (wcsstr(_a, _b) == (_a))
|
|
|
|
|
|
#define WCS_CAT7(_dest, _a1, _a2, _a3, _a4, _a5, _a6, _a7 ) \
|
|
wcscpy(_dest, _a1); \
|
|
wcscat(_dest, _a2); \
|
|
wcscat(_dest, _a3); \
|
|
wcscat(_dest, _a4); \
|
|
wcscat(_dest, _a5); \
|
|
wcscat(_dest, _a6); \
|
|
wcscat(_dest, _a7);
|
|
|
|
|
|
//
|
|
// Some useful ldap macroes
|
|
//
|
|
#define LDAP_FREE_MSG(x) {if (x) {ldap_msgfree(x); (x) = NULL;}}
|
|
#define LDAP_FREE_VALUES(x) {if (x) {ldap_value_free(x); (x) = NULL;}}
|
|
#define LDAP_FREE_BER_VALUES(x) {if (x) {ldap_value_free_len(x); (x) = NULL;}}
|
|
|
|
|
|
//Global data
|
|
BOOL bVerboseMode = FALSE;
|
|
BOOL bVerboseModeSearch = FALSE;
|
|
PWCHAR DcName = NULL;
|
|
PLDAP pLdap = NULL;
|
|
BOOL bDebugMode = FALSE;
|
|
BOOL bAffectAll = FALSE;
|
|
PBYTE SchedMask = NULL;
|
|
PBYTE SchedOverride = NULL;
|
|
BOOL bCreateSet = FALSE;
|
|
BOOL bUpdateSet = FALSE;
|
|
BOOL bDelSet = FALSE;
|
|
BOOL bCreateMember = FALSE;
|
|
BOOL bMakeMePrimary = FALSE;
|
|
BOOL bUpdateMember = FALSE;
|
|
BOOL bDelMember = FALSE;
|
|
BOOL bCreateSubscriber = FALSE;
|
|
BOOL bUpdateSubscriber = FALSE;
|
|
BOOL bDelSubscriber = FALSE;
|
|
BOOL bDump = FALSE;
|
|
BOOL bSchedule = FALSE;
|
|
BOOL bDFSNaming = FALSE;
|
|
|
|
WCHAR ComputerObjectGuidStr[50];
|
|
PWCHAR DomainGuidStr = NULL; // [50];
|
|
PWCHAR DFSName = NULL;
|
|
|
|
//
|
|
// Static array to print the replica set type.
|
|
//
|
|
|
|
WCHAR ReplicaSetTypeStr[][MAX_PATH] = {L"Invalid type",
|
|
L"Invalid type",
|
|
L"Sysvol",
|
|
L"Dfs",
|
|
L"Other"};
|
|
|
|
|
|
DWORD
|
|
UpdateReplicaSet(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName,
|
|
PWCHAR ReplicaSetType,
|
|
PWCHAR FileFilter,
|
|
PWCHAR DirectoryFilter,
|
|
PWCHAR PrimaryMember,
|
|
PBYTE pSchedule
|
|
);
|
|
|
|
VOID
|
|
PrintScheduleGrid(
|
|
PUCHAR ScheduleData,
|
|
DWORD Mask
|
|
);
|
|
|
|
VOID
|
|
PrintSchedule(
|
|
PSCHEDULE Schedule,
|
|
DWORD Mask
|
|
);
|
|
|
|
|
|
DWORD
|
|
BindToDC (
|
|
IN PWCHAR pszDC,
|
|
OUT PLDAP *ppLDAP
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets up an LDAP connection to the specified server
|
|
|
|
Arguments:
|
|
|
|
pwszDC - DS DC to bind to
|
|
ppLDAP - The LDAP connection information is returned here
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS - Success
|
|
|
|
--*/
|
|
{
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
ULONG ulOptions;
|
|
|
|
//
|
|
// if ldap_open is called with a server name the api will call DsGetDcName
|
|
// passing the server name as the domainname parm...bad, because
|
|
// DsGetDcName will make a load of DNS queries based on the server name,
|
|
// it is designed to construct these queries from a domain name...so all
|
|
// these queries will be bogus, meaning they will waste network bandwidth,
|
|
// time to fail, and worst case cause expensive on demand links to come up
|
|
// as referrals/forwarders are contacted to attempt to resolve the bogus
|
|
// names. By setting LDAP_OPT_AREC_EXCLUSIVE to on using ldap_set_option
|
|
// after the ldap_init but before any other operation using the ldap
|
|
// handle from ldap_init, the delayed connection setup will not call
|
|
// DsGetDcName, just gethostbyname, or if an IP is passed, the ldap client
|
|
// will detect that and use the address directly.
|
|
//
|
|
// *ppLDAP = ldap_open(pszDC, LDAP_PORT);
|
|
*ppLDAP = ldap_init(pszDC, LDAP_PORT);
|
|
|
|
if(*ppLDAP == NULL) {
|
|
dwErr = ERROR_PATH_NOT_FOUND;
|
|
} else {
|
|
//
|
|
// set the options.
|
|
//
|
|
ulOptions = PtrToUlong(LDAP_OPT_ON);
|
|
ldap_set_option(*ppLDAP, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
|
|
|
|
//
|
|
// Do a bind...
|
|
//
|
|
dwErr = ldap_bind_s(*ppLDAP, NULL, NULL, LDAP_AUTH_NEGOTIATE);
|
|
}
|
|
|
|
return(dwErr);
|
|
}
|
|
|
|
PWCHAR
|
|
FrsWcsDup(
|
|
PWCHAR OldStr
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Duplicate a string using our memory allocater
|
|
|
|
Arguments:
|
|
OldArg - string to duplicate
|
|
|
|
Return Value:
|
|
Duplicated string. Free with FrsFree().
|
|
--*/
|
|
{
|
|
PWCHAR NewStr;
|
|
|
|
//
|
|
// E.g., when duplicating NodePartner when none exists
|
|
//
|
|
if (OldStr == NULL)
|
|
return NULL;
|
|
|
|
NewStr = (PWCHAR)malloc((wcslen(OldStr) + 1) * sizeof(WCHAR));
|
|
wcscpy(NewStr, OldStr);
|
|
|
|
return NewStr;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
AddMod(
|
|
PWCHAR AttrType,
|
|
PWCHAR AttrValue,
|
|
LDAPMod ***pppMod
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Add an attribute (plus values) to a structure that will eventually be
|
|
used in an ldap_add() function to add an object to the DS. The null-
|
|
terminated array referenced by pppMod grows with each call to this
|
|
routine. The array is freed by the caller using FreeMod().
|
|
|
|
Arguments:
|
|
AttrType - The object class of the object.
|
|
AttrValue - The value of the attribute.
|
|
pppMod - Address of an array of pointers to "attributes". Don't
|
|
give me that look -- this is an LDAP thing.
|
|
|
|
Return Value:
|
|
The pppMod array grows by one entry. The caller must free it with
|
|
FreeMod().
|
|
--*/
|
|
{
|
|
DWORD NumMod; // Number of entries in the Mod array
|
|
LDAPMod **ppMod; // Address of the first entry in the Mod array
|
|
LDAPMod *Attr; // An attribute structure
|
|
PWCHAR *Values; // An array of pointers to bervals
|
|
|
|
if (AttrValue == NULL)
|
|
return;
|
|
|
|
//
|
|
// The null-terminated array doesn't exist; create it
|
|
//
|
|
if (*pppMod == NULL) {
|
|
*pppMod = (LDAPMod **)malloc(sizeof (*pppMod));
|
|
**pppMod = NULL;
|
|
}
|
|
|
|
//
|
|
// Increase the array's size by 1
|
|
//
|
|
for (ppMod = *pppMod, NumMod = 2; *ppMod != NULL; ++ppMod, ++NumMod);
|
|
*pppMod = (LDAPMod **)realloc(*pppMod, sizeof (*pppMod) * NumMod);
|
|
|
|
//
|
|
// Add the new attribute + value to the Mod array
|
|
//
|
|
Values = (PWCHAR *)malloc(sizeof (PWCHAR) * 2);
|
|
Values[0] = _wcsdup(AttrValue);
|
|
Values[1] = NULL;
|
|
|
|
Attr = (LDAPMod *)malloc(sizeof (*Attr));
|
|
Attr->mod_values = Values;
|
|
Attr->mod_type = _wcsdup(AttrType);
|
|
// Attr->mod_op = LDAP_MOD_ADD;
|
|
Attr->mod_op = LDAP_MOD_REPLACE;
|
|
(*pppMod)[NumMod - 1] = NULL;
|
|
(*pppMod)[NumMod - 2] = Attr;
|
|
}
|
|
|
|
VOID
|
|
AddBerMod(
|
|
PWCHAR AttrType,
|
|
PCHAR AttrValue,
|
|
DWORD AttrValueLen,
|
|
LDAPMod ***pppMod
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Add an attribute (plus values) to a structure that will eventually be
|
|
used in an ldap_add() function to add an object to the DS. The null-
|
|
terminated array referenced by pppMod grows with each call to this
|
|
routine. The array is freed by the caller using FreeMod().
|
|
|
|
Arguments:
|
|
AttrType - The object class of the object.
|
|
AttrValue - The value of the attribute.
|
|
AttrValueLen - length of the attribute
|
|
pppMod - Address of an array of pointers to "attributes". Don't
|
|
give me that look -- this is an LDAP thing.
|
|
|
|
Return Value:
|
|
The pppMod array grows by one entry. The caller must free it with
|
|
FreeMod().
|
|
--*/
|
|
{
|
|
DWORD NumMod; // Number of entries in the Mod array
|
|
LDAPMod **ppMod; // Address of the first entry in the Mod array
|
|
LDAPMod *Attr; // An attribute structure
|
|
PLDAP_BERVAL Berval;
|
|
PLDAP_BERVAL *Values; // An array of pointers to bervals
|
|
|
|
if (AttrValue == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// The null-terminated array doesn't exist; create it
|
|
//
|
|
if (*pppMod == NULL) {
|
|
*pppMod = (LDAPMod **)malloc(sizeof (*pppMod));
|
|
**pppMod = NULL;
|
|
}
|
|
|
|
//
|
|
// Increase the array's size by 1
|
|
//
|
|
for (ppMod = *pppMod, NumMod = 2; *ppMod != NULL; ++ppMod, ++NumMod);
|
|
*pppMod = (LDAPMod **)realloc(*pppMod, sizeof (*pppMod) * NumMod);
|
|
|
|
//
|
|
// Construct a berval
|
|
//
|
|
Berval = (PLDAP_BERVAL)malloc(sizeof(LDAP_BERVAL));
|
|
Berval->bv_len = AttrValueLen;
|
|
Berval->bv_val = (PCHAR)malloc(AttrValueLen);
|
|
CopyMemory(Berval->bv_val, AttrValue, AttrValueLen);
|
|
|
|
//
|
|
// Add the new attribute + value to the Mod array
|
|
//
|
|
Values = (PLDAP_BERVAL *)malloc(sizeof (PLDAP_BERVAL) * 2);
|
|
Values[0] = Berval;
|
|
Values[1] = NULL;
|
|
|
|
Attr = (LDAPMod *)malloc(sizeof (*Attr));
|
|
Attr->mod_bvalues = Values;
|
|
Attr->mod_type = _wcsdup(AttrType);
|
|
Attr->mod_op = LDAP_MOD_BVALUES | LDAP_MOD_REPLACE;
|
|
|
|
(*pppMod)[NumMod - 1] = NULL;
|
|
(*pppMod)[NumMod - 2] = Attr;
|
|
}
|
|
|
|
VOID
|
|
FreeMod(
|
|
LDAPMod ***pppMod
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Free the structure built by successive calls to AddMod().
|
|
|
|
Arguments:
|
|
pppMod - Address of a null-terminated array.
|
|
|
|
Return Value:
|
|
*pppMod set to NULL.
|
|
--*/
|
|
{
|
|
DWORD i, j;
|
|
LDAPMod **ppMod;
|
|
|
|
if (!pppMod || !*pppMod) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// For each attibute
|
|
//
|
|
ppMod = *pppMod;
|
|
for (i = 0; ppMod[i] != NULL; ++i) {
|
|
//
|
|
// For each value of the attribute
|
|
//
|
|
for (j = 0; (ppMod[i])->mod_values[j] != NULL; ++j) {
|
|
//
|
|
// Free the value
|
|
//
|
|
if (ppMod[i]->mod_op & LDAP_MOD_BVALUES) {
|
|
free(ppMod[i]->mod_bvalues[j]->bv_val);
|
|
}
|
|
free((ppMod[i])->mod_values[j]);
|
|
}
|
|
free((ppMod[i])->mod_values); // Free the array of pointers to values
|
|
free((ppMod[i])->mod_type); // Free the string identifying the attribute
|
|
free(ppMod[i]); // Free the attribute
|
|
}
|
|
free(ppMod); // Free the array of pointers to attributes
|
|
*pppMod = NULL; // Now ready for more calls to AddMod()
|
|
}
|
|
|
|
BOOL
|
|
LdapSearch(
|
|
PLDAP ldap,
|
|
PWCHAR Base,
|
|
ULONG Scope,
|
|
PWCHAR Filter,
|
|
PWCHAR Attrs[],
|
|
ULONG AttrsOnly,
|
|
LDAPMessage **Res,
|
|
BOOL Quiet
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Issue the ldap ldap_search_s call, check for errors, and check for
|
|
a shutdown in progress.
|
|
|
|
Arguments:
|
|
ldap
|
|
Base
|
|
Scope
|
|
Filter
|
|
Attrs
|
|
AttrsOnly
|
|
Res
|
|
|
|
Return Value:
|
|
The ldap array of values or NULL if the Base, DesiredAttr, or its values
|
|
does not exist. The ldap array is freed with LDAP_FREE_VALUES().
|
|
--*/
|
|
{
|
|
DWORD LStatus;
|
|
|
|
//
|
|
// Issue the ldap search
|
|
//
|
|
LStatus = ldap_search_s(ldap, Base, Scope, Filter, Attrs, AttrsOnly, Res);
|
|
|
|
//
|
|
// Check for errors
|
|
//
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
if (!Quiet) {
|
|
DPRINT4("WARN - Error searching %ws for %ws; LStatus %d: %ws\n",
|
|
Base, Filter, LStatus, ldap_err2string(LStatus));
|
|
}
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Return TRUE if not shutting down
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
PWCHAR *
|
|
GetValues(
|
|
IN PLDAP Ldap,
|
|
IN PWCHAR Dn,
|
|
IN PWCHAR DesiredAttr,
|
|
IN BOOL Quiet
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Return the DS values for one attribute in an object.
|
|
|
|
Arguments:
|
|
ldap - An open, bound ldap port.
|
|
Base - The "pathname" of a DS object.
|
|
DesiredAttr - Return values for this attribute.
|
|
Quiet
|
|
|
|
Return Value:
|
|
An array of char pointers that represents the values for the attribute.
|
|
The caller must free the array with LDAP_FREE_VALUES(). NULL if unsuccessful.
|
|
--*/
|
|
{
|
|
PWCHAR Attr;
|
|
BerElement *Ber;
|
|
PLDAPMessage LdapMsg;
|
|
PLDAPMessage LdapEntry;
|
|
PWCHAR Attrs[2];
|
|
PWCHAR *Values = NULL;
|
|
|
|
//
|
|
// Search Base for all of its attributes + values
|
|
//
|
|
MK_ATTRS_1(Attrs, DesiredAttr);
|
|
|
|
//
|
|
// Issue the ldap search
|
|
//
|
|
if (!LdapSearch(Ldap, Dn, LDAP_SCOPE_BASE, CLASS_ANY, Attrs, 0, &LdapMsg, Quiet)) {
|
|
return NULL;
|
|
}
|
|
LdapEntry = ldap_first_entry(Ldap, LdapMsg);
|
|
if (LdapEntry) {
|
|
Attr = ldap_first_attribute(Ldap, LdapEntry, &Ber);
|
|
if (Attr) {
|
|
Values = ldap_get_values(Ldap, LdapEntry, Attr);
|
|
}
|
|
}
|
|
LDAP_FREE_MSG(LdapMsg);
|
|
return Values;
|
|
}
|
|
|
|
|
|
|
|
PLDAP_BERVAL *
|
|
GetBerValues(
|
|
IN PLDAP Ldap,
|
|
IN PWCHAR Dn,
|
|
IN PWCHAR DesiredAttr,
|
|
IN BOOL Quiet
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Return the DS Ber values for one binary attribute in an object.
|
|
|
|
Arguments:
|
|
ldap - An open, bound ldap port.
|
|
Base - The "pathname" of a DS object.
|
|
DesiredAttr - Return values for this attribute.
|
|
Quiet
|
|
|
|
Return Value:
|
|
An array of BerVal struct pointers that represents the values for the attribute.
|
|
The caller must free the array with LDAP_FREE_BER_VALUES(). NULL if unsuccessful.
|
|
--*/
|
|
{
|
|
PWCHAR Attr;
|
|
BerElement *Ber;
|
|
PLDAPMessage LdapMsg;
|
|
PLDAPMessage LdapEntry;
|
|
PWCHAR Attrs[2];
|
|
PLDAP_BERVAL *Values = NULL;
|
|
|
|
//
|
|
// Search Base for all of its attributes + values
|
|
//
|
|
MK_ATTRS_1(Attrs, DesiredAttr);
|
|
|
|
//
|
|
// Issue the ldap search
|
|
//
|
|
if (!LdapSearch(Ldap, Dn, LDAP_SCOPE_BASE, CLASS_ANY, Attrs, 0, &LdapMsg, Quiet)) {
|
|
return NULL;
|
|
}
|
|
|
|
LdapEntry = ldap_first_entry(Ldap, LdapMsg);
|
|
if (LdapEntry) {
|
|
Attr = ldap_first_attribute(Ldap, LdapEntry, &Ber);
|
|
if (Attr) {
|
|
Values = ldap_get_values_len(Ldap, LdapEntry, Attr);
|
|
}
|
|
}
|
|
LDAP_FREE_MSG(LdapMsg);
|
|
return Values;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
LdapSearchInit(
|
|
PLDAP ldap,
|
|
PWCHAR Base,
|
|
ULONG Scope,
|
|
PWCHAR Filter,
|
|
PWCHAR Attrs[],
|
|
ULONG AttrsOnly,
|
|
PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext,
|
|
BOOL Quiet
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Issue the ldap ldap_search_s call, check for errors, and check for
|
|
a shutdown in progress.
|
|
|
|
Arguments:
|
|
ldap
|
|
Base
|
|
Scope
|
|
Filter
|
|
Attrs
|
|
AttrsOnly
|
|
Res
|
|
FrsSearchContext
|
|
|
|
Return Value:
|
|
The ldap array of values or NULL if the Base, DesiredAttr, or its values
|
|
does not exist. The ldap array is freed with LDAP_FREE_VALUES().
|
|
--*/
|
|
{
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
PLDAPControl ServerControls[2];
|
|
berval cookie1 = { 0, NULL };
|
|
|
|
FrsSearchContext->bOpen = FALSE;
|
|
FrsSearchContext->LdapMsg = NULL;
|
|
FrsSearchContext->EntriesInPage = 0;
|
|
FrsSearchContext->CurrentEntry = 0;
|
|
FrsSearchContext->TotalEntries = 0;
|
|
|
|
FrsSearchContext->Filter = FrsWcsDup(Filter);
|
|
FrsSearchContext->BaseDn = FrsWcsDup(Base);
|
|
FrsSearchContext->Scope = Scope;
|
|
FrsSearchContext->PageSize = FRS_LDAP_SEARCH_PAGESIZE;
|
|
FrsSearchContext->Attrs = Attrs;
|
|
|
|
|
|
LStatus = ldap_create_page_control(ldap,
|
|
FrsSearchContext->PageSize,
|
|
&cookie1,
|
|
FALSE, // is critical
|
|
&ServerControls[0]
|
|
);
|
|
|
|
ServerControls[1] = NULL;
|
|
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
DPRINT1("ldap_create_page_control returned error. LStatus = 0x%x\n", LStatus);
|
|
}
|
|
|
|
LStatus = ldap_search_ext_s(ldap,
|
|
FrsSearchContext->BaseDn,
|
|
FrsSearchContext->Scope,
|
|
FrsSearchContext->Filter,
|
|
FrsSearchContext->Attrs,
|
|
FALSE,
|
|
ServerControls,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
&FrsSearchContext->LdapMsg);
|
|
|
|
if (LStatus == LDAP_SUCCESS) {
|
|
FrsSearchContext->EntriesInPage = ldap_count_entries(ldap, FrsSearchContext->LdapMsg);
|
|
FrsSearchContext->CurrentEntry = 0;
|
|
FrsSearchContext->bOpen = TRUE;
|
|
}
|
|
|
|
|
|
return (LStatus == LDAP_SUCCESS);
|
|
}
|
|
|
|
PLDAPMessage
|
|
LdapSearchGetNextEntry(
|
|
PLDAP ldap,
|
|
PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Issue the ldap ldap_search_s call, check for errors, and check for
|
|
a shutdown in progress.
|
|
|
|
Arguments:
|
|
ldap
|
|
FrsSearchContext
|
|
|
|
Return Value:
|
|
The ldap array of values or NULL if the Base, DesiredAttr, or its values
|
|
does not exist. The ldap array is freed with LDAP_FREE_VALUES().
|
|
--*/
|
|
{
|
|
|
|
FrsSearchContext->CurrentEntry += 1;
|
|
if ( FrsSearchContext->CurrentEntry == 1 ) {
|
|
FrsSearchContext->CurrentLdapMsg = ldap_first_entry(ldap ,FrsSearchContext->LdapMsg);
|
|
} else {
|
|
FrsSearchContext->CurrentLdapMsg = ldap_next_entry(ldap ,FrsSearchContext->CurrentLdapMsg);
|
|
}
|
|
|
|
return FrsSearchContext->CurrentLdapMsg;
|
|
}
|
|
|
|
DWORD
|
|
LdapSearchGetNextPage(
|
|
PLDAP ldap,
|
|
PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Issue the ldap ldap_search_s call, check for errors, and check for
|
|
a shutdown in progress.
|
|
|
|
Arguments:
|
|
ldap
|
|
FrsSearchContext
|
|
|
|
Return Value:
|
|
The ldap array of values or NULL if the Base, DesiredAttr, or its values
|
|
does not exist. The ldap array is freed with LDAP_FREE_VALUES().
|
|
--*/
|
|
{
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
berval * CurrCookie = NULL;
|
|
PLDAPControl * CurrControls = NULL;
|
|
ULONG retcode = 0;
|
|
PLDAPControl ServerControls[2];
|
|
|
|
|
|
|
|
// Get the server control from the message, and make a new control with the cookie from the server
|
|
LStatus = ldap_parse_result(ldap, FrsSearchContext->LdapMsg, &retcode,NULL,NULL,NULL,&CurrControls,FALSE);
|
|
LDAP_FREE_MSG(FrsSearchContext->LdapMsg);
|
|
FrsSearchContext->LdapMsg = NULL;
|
|
|
|
if (LStatus == LDAP_SUCCESS) {
|
|
LStatus = ldap_parse_page_control(ldap, CurrControls, &FrsSearchContext->TotalEntries, &CurrCookie);
|
|
// under Exchange 5.5, before SP 2, this will fail with LDAP_CONTROL_NOT_FOUND when there are
|
|
// no more search results. With Exchange 5.5 SP 2, this succeeds, and gives us a cookie that will
|
|
// cause us to start over at the beginning of the search results.
|
|
|
|
}
|
|
if (LStatus == LDAP_SUCCESS) {
|
|
if ( CurrCookie->bv_len == 0 && CurrCookie->bv_val == 0 ) {
|
|
// under Exchange 5.5, SP 2, this means we're at the end of the results.
|
|
// if we pass in this cookie again, we will start over at the beginning of the search results.
|
|
LStatus = LDAP_CONTROL_NOT_FOUND;
|
|
}
|
|
|
|
ServerControls[0] = NULL;
|
|
ServerControls[1] = NULL;
|
|
if (LStatus == LDAP_SUCCESS) {
|
|
LStatus = ldap_create_page_control(ldap,
|
|
FrsSearchContext->PageSize,
|
|
CurrCookie,
|
|
FALSE,
|
|
ServerControls);
|
|
}
|
|
ldap_controls_free(CurrControls);
|
|
CurrControls = NULL;
|
|
ber_bvfree(CurrCookie);
|
|
CurrCookie = NULL;
|
|
}
|
|
|
|
// continue the search with the new cookie
|
|
if (LStatus == LDAP_SUCCESS) {
|
|
LStatus = ldap_search_ext_s(ldap,
|
|
FrsSearchContext->BaseDn,
|
|
FrsSearchContext->Scope,
|
|
FrsSearchContext->Filter,
|
|
FrsSearchContext->Attrs,
|
|
FALSE,
|
|
ServerControls,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
&FrsSearchContext->LdapMsg);
|
|
|
|
if ( (LStatus != LDAP_SUCCESS) && (LStatus != LDAP_CONTROL_NOT_FOUND) )
|
|
{
|
|
// LDAP_CONTROL_NOT_FOUND means that we have reached the end of the search results
|
|
// in Exchange 5.5, before SP 2 (the server doesn't return a page control when there
|
|
// are no more pages, so we get LDAP_CONTROL_NOT_FOUND when we try to extract the page
|
|
// control from the search results).
|
|
|
|
}
|
|
}
|
|
|
|
if (LStatus == LDAP_SUCCESS) {
|
|
FrsSearchContext->EntriesInPage = ldap_count_entries(ldap, FrsSearchContext->LdapMsg);
|
|
FrsSearchContext->CurrentEntry = 0;
|
|
|
|
}
|
|
|
|
return LdapMapErrorToWin32(LStatus);
|
|
}
|
|
|
|
PLDAPMessage
|
|
LdapSearchNext(
|
|
PLDAP ldap,
|
|
PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Issue the ldap ldap_search_s call, check for errors, and check for
|
|
a shutdown in progress.
|
|
|
|
Arguments:
|
|
ldap
|
|
FrsSearchContext
|
|
|
|
Return Value:
|
|
The ldap array of values or NULL if the Base, DesiredAttr, or its values
|
|
does not exist. The ldap array is freed with LDAP_FREE_VALUES().
|
|
--*/
|
|
{
|
|
DWORD WStatus = ERROR_SUCCESS;
|
|
PLDAPMessage NextEntry = NULL;
|
|
|
|
if (FrsSearchContext->bOpen != TRUE) {
|
|
NextEntry = NULL;
|
|
} else {
|
|
if (FrsSearchContext->EntriesInPage > FrsSearchContext->CurrentEntry ) {
|
|
// return the next entry from the current page
|
|
return LdapSearchGetNextEntry(ldap, FrsSearchContext);
|
|
} else {
|
|
// see if there are more pages of results to get
|
|
WStatus = LdapSearchGetNextPage(ldap, FrsSearchContext);
|
|
if (WStatus == ERROR_SUCCESS) {
|
|
return LdapSearchGetNextEntry(ldap, FrsSearchContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NextEntry;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
LdapSearchClose(
|
|
PFRS_LDAP_SEARCH_CONTEXT FrsSearchContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Issue the ldap ldap_search_s call, check for errors, and check for
|
|
a shutdown in progress.
|
|
|
|
Arguments:
|
|
ldap
|
|
FrsSearchContext
|
|
|
|
Return Value:
|
|
The ldap array of values or NULL if the Base, DesiredAttr, or its values
|
|
does not exist. The ldap array is freed with LDAP_FREE_VALUES().
|
|
--*/
|
|
{
|
|
FrsSearchContext->bOpen = FALSE;
|
|
FrsSearchContext->EntriesInPage = 0;
|
|
FrsSearchContext->CurrentEntry = 0;
|
|
FrsSearchContext->TotalEntries = 0;
|
|
|
|
FREE(FrsSearchContext->Filter);
|
|
FREE(FrsSearchContext->BaseDn);
|
|
|
|
if (FrsSearchContext->LdapMsg != NULL) {
|
|
LDAP_FREE_MSG(FrsSearchContext->LdapMsg);
|
|
}
|
|
}
|
|
|
|
PWCHAR
|
|
GetRootDn(
|
|
PLDAP Ldap,
|
|
PWCHAR NamingContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
PWCHAR Root; // DS pathname of configuration container
|
|
PWCHAR *Values; // values from the attribute "namingContexts"
|
|
DWORD NumVals; // number of values
|
|
|
|
//
|
|
// Return all of the values for the attribute namingContexts
|
|
//
|
|
Values = GetValues(Ldap, ATTR_ROOT, ATTR_NAMING_CONTEXTS, FALSE);
|
|
if (Values == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Find the naming context that begins with CN=Configuration
|
|
//
|
|
NumVals = ldap_count_values(Values);
|
|
while (NumVals--) {
|
|
Root = wcsstr(Values[NumVals], NamingContext);
|
|
if (Root != NULL && Root == Values[NumVals]) {
|
|
Root = FrsWcsDup(Root);
|
|
LDAP_FREE_VALUES(Values);
|
|
return Root;
|
|
}
|
|
}
|
|
DPRINT1("ERROR - COULD NOT FIND %ws\n", NamingContext);
|
|
LDAP_FREE_VALUES(Values);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
PWCHAR
|
|
ExtendDn(
|
|
PWCHAR Dn,
|
|
PWCHAR Cn
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Extend an existing DN with a new CN= component.
|
|
|
|
Arguments:
|
|
Dn - distinguished name
|
|
Cn - common name
|
|
|
|
Return Value:
|
|
CN=Cn,Dn
|
|
--*/
|
|
{
|
|
ULONG Len;
|
|
PWCHAR NewDn;
|
|
|
|
Len = wcslen(L"CN=,") + wcslen(Dn) + wcslen(Cn) + 1;
|
|
NewDn = (PWCHAR)malloc(Len * sizeof(WCHAR));
|
|
wcscpy(NewDn, L"CN=");
|
|
wcscat(NewDn, Cn);
|
|
wcscat(NewDn, L",");
|
|
wcscat(NewDn, Dn);
|
|
return NewDn;
|
|
}
|
|
|
|
|
|
PVOID *
|
|
FindValues(
|
|
PLDAPMessage LdapEntry,
|
|
PWCHAR DesiredAttr,
|
|
BOOL DoBerValues
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Return the DS values for one attribute in an entry.
|
|
|
|
Arguments:
|
|
Ldap - An open, bound ldap port.
|
|
LdapEntry - An ldap entry returned by ldap_search_s()
|
|
DesiredAttr - Return values for this attribute.
|
|
DoVerValues - Return the bervals
|
|
|
|
Return Value:
|
|
An array of char pointers that represents the values for the attribute.
|
|
The caller must free the array with LDAP_FREE_VALUES(). NULL if unsuccessful.
|
|
--*/
|
|
{
|
|
PWCHAR LdapAttr; // Retrieved from an ldap entry
|
|
BerElement *Ber; // Needed for scanning attributes
|
|
|
|
//
|
|
// Search the entry for the desired attribute
|
|
//
|
|
for (LdapAttr = ldap_first_attribute(pLdap, LdapEntry, &Ber);
|
|
LdapAttr != NULL;
|
|
LdapAttr = ldap_next_attribute(pLdap, LdapEntry, Ber)) {
|
|
if (_wcsicmp(DesiredAttr, LdapAttr) == 0) {
|
|
//
|
|
// Return the values for DesiredAttr
|
|
//
|
|
if (DoBerValues) {
|
|
return (PVOID *)ldap_get_values_len(pLdap, LdapEntry, LdapAttr);
|
|
} else {
|
|
return (PVOID *)ldap_get_values(pLdap, LdapEntry, LdapAttr);
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PWCHAR
|
|
FindValue(
|
|
PLDAPMessage LdapEntry,
|
|
PWCHAR DesiredAttr
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Return a copy of the first DS value for one attribute in an entry.
|
|
|
|
Arguments:
|
|
Ldap - An open, bound ldap port.
|
|
LdapEntry - An ldap entry returned by ldap_search_s()
|
|
DesiredAttr - Return values for this attribute.
|
|
|
|
Return Value:
|
|
A zero-terminated string or NULL if the attribute or its value
|
|
doesn't exist. The string is freed with FREE().
|
|
--*/
|
|
{
|
|
PWCHAR Val;
|
|
PWCHAR *Values;
|
|
|
|
// Get ldap's array of values
|
|
Values = (PWCHAR *)FindValues(LdapEntry, DesiredAttr, FALSE);
|
|
|
|
// Copy the first value (if any)
|
|
if (Values) {
|
|
Val = new WCHAR[wcslen(Values[0]) + 1];
|
|
wcscpy(Val, Values[0]);
|
|
} else {
|
|
Val = NULL;
|
|
}
|
|
|
|
// Free ldap's array of values
|
|
LDAP_FREE_VALUES(Values);
|
|
|
|
return Val;
|
|
}
|
|
|
|
BOOL
|
|
FindBerValue(
|
|
PLDAPMessage Entry,
|
|
PWCHAR DesiredAttr,
|
|
ULONG *Len,
|
|
VOID **Value
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Return a copy of the attributes object's schedule
|
|
|
|
Arguments:
|
|
ldap - An open, bound ldap port.
|
|
Entry - An ldap entry returned by ldap_search_s()
|
|
DesiredAttr - desired attribute
|
|
Len - length of Value
|
|
Value - binary value
|
|
|
|
Return Value:
|
|
The address of a schedule or NULL. Free with FrsFree().
|
|
--*/
|
|
{
|
|
PLDAP_BERVAL *Values;
|
|
|
|
*Len = 0;
|
|
*Value = NULL;
|
|
|
|
//
|
|
// Get ldap's array of values
|
|
//
|
|
Values = (PLDAP_BERVAL *)FindValues(Entry, DesiredAttr, TRUE);
|
|
if (!Values) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Return a copy of the schedule
|
|
//
|
|
*Len = Values[0]->bv_len;
|
|
if (*Len) {
|
|
*Value = new WCHAR[*Len];
|
|
CopyMemory(*Value, Values[0]->bv_val, *Len);
|
|
}
|
|
LDAP_FREE_BER_VALUES(Values);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PBYTE
|
|
ReadScheduleFile(
|
|
PWCHAR FileName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Read a Hex formated byte array for a 7x24 schedule from a file.
|
|
Convert to a 7x24 binary schedule.
|
|
|
|
Arguments:
|
|
FileName - Name of schedule file.
|
|
|
|
Return Value:
|
|
ptr to schedule data array or NULL if invalid param.
|
|
--*/
|
|
{
|
|
HANDLE FileHandle;
|
|
PBYTE SchedData;
|
|
DWORD WStatus;
|
|
DWORD BytesRead;
|
|
DWORD DataByte, i, Day, Hour;
|
|
ULONG Remaining;
|
|
PCHAR pTC;
|
|
CHAR SchedText[FRST_SIZE_OF_SCHEDULE_GRID*3 + 50];
|
|
|
|
SchedData = new BYTE[FRST_SIZE_OF_SCHEDULE_GRID];
|
|
if (SchedData == NULL) {
|
|
printf("Error allocating memory.\n");
|
|
return NULL;
|
|
}
|
|
|
|
FileHandle = CreateFileW(
|
|
FileName, // lpszName
|
|
GENERIC_READ | GENERIC_WRITE, // fdwAccess
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, // fdwShareMode
|
|
NULL, // lpsa
|
|
OPEN_EXISTING, // fdwCreate
|
|
FILE_ATTRIBUTE_NORMAL, // fdwAttrAndFlags
|
|
NULL); // hTemplateFile
|
|
|
|
if (!HANDLE_IS_VALID(FileHandle)) {
|
|
WStatus = GetLastError();
|
|
printf("Error opening %ws WStatus: %d\n", FileName, WStatus);
|
|
FREE(SchedData);
|
|
return NULL;
|
|
}
|
|
|
|
if (!ReadFile(FileHandle, SchedText, sizeof(SchedText), &BytesRead, NULL)) {
|
|
WStatus = GetLastError();
|
|
printf("Error reading %ws WStatus: %d\n", FileName, WStatus);
|
|
FREE(SchedData);
|
|
FRS_CLOSE(FileHandle);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// remove any white-space chars, including CR-LF, used for formatting.
|
|
//
|
|
Remaining = BytesRead;
|
|
pTC = SchedText;
|
|
|
|
while (Remaining > 0) {
|
|
if (isspace((LONG) *pTC)) {
|
|
memcpy(pTC, pTC+1, Remaining-1);
|
|
} else {
|
|
pTC++;
|
|
}
|
|
Remaining -= 1;
|
|
}
|
|
BytesRead = pTC - SchedText;
|
|
|
|
if (BytesRead < FRST_SIZE_OF_SCHEDULE_GRID*2) {
|
|
printf("Error reading %ws Expecting %d bytes, Actual was %d bytes. Could be too much whitespace.\n",
|
|
FileName, FRST_SIZE_OF_SCHEDULE_GRID*2, BytesRead);
|
|
FREE(SchedData);
|
|
FRS_CLOSE(FileHandle);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Result should be exactly the right size.
|
|
//
|
|
if (BytesRead != FRST_SIZE_OF_SCHEDULE_GRID*2) {
|
|
printf("Error reading %ws Expecting %d bytes, Actual was %d bytes\n",
|
|
FileName, FRST_SIZE_OF_SCHEDULE_GRID*2, BytesRead);
|
|
FREE(SchedData);
|
|
FRS_CLOSE(FileHandle);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Convert to binary.
|
|
//
|
|
for (i=0; i<FRST_SIZE_OF_SCHEDULE_GRID; i++) {
|
|
if (sscanf(&SchedText[i*2], "%2x", &DataByte) != 1) {
|
|
SchedText[i*2+2] = '\0';
|
|
printf("Error reading %ws Invalid hex data (%s) for schedule byte %d.\n",
|
|
FileName, &SchedText[i*2], i);
|
|
FREE(SchedData);
|
|
FRS_CLOSE(FileHandle);
|
|
return NULL;
|
|
}
|
|
SchedData[i] = (BYTE) DataByte;
|
|
}
|
|
|
|
|
|
FRS_CLOSE(FileHandle);
|
|
return SchedData;
|
|
}
|
|
|
|
DWORD
|
|
BuildSchedule(
|
|
PBYTE *ppSchedule,
|
|
DWORD Interval,
|
|
DWORD Stagger,
|
|
DWORD Offset
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Build the schedule structure.
|
|
Arguments:
|
|
ppSchedule - pointer to return the schedule in.
|
|
|
|
Return Value:
|
|
winerror.
|
|
--*/
|
|
{
|
|
BYTE ScheduleData[FRST_SIZE_OF_SCHEDULE_GRID];
|
|
UINT i, Day, Hour;
|
|
DWORD Status = ERROR_SUCCESS;
|
|
|
|
*ppSchedule = new BYTE[FRST_SIZE_OF_SCHEDULE];
|
|
if (*ppSchedule == NULL) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
//
|
|
// Set the values for the elements of the SCHEDULE structure.
|
|
//
|
|
|
|
((PSCHEDULE)*ppSchedule)->Size = FRST_SIZE_OF_SCHEDULE;
|
|
((PSCHEDULE)*ppSchedule)->Bandwidth = 0;
|
|
((PSCHEDULE)*ppSchedule)->NumberOfSchedules = 1;
|
|
((PSCHEDULE)*ppSchedule)->Schedules->Type = SCHEDULE_INTERVAL;
|
|
((PSCHEDULE)*ppSchedule)->Schedules->Offset = sizeof(SCHEDULE);
|
|
|
|
//
|
|
// Building the ScheduleData array depending on the requested schedule.
|
|
//
|
|
|
|
for (i=0 ; i<FRST_SIZE_OF_SCHEDULE_GRID ; ++i) {
|
|
if (Interval == 0) {
|
|
ScheduleData[i] = 0x00;
|
|
} else if (i < (Offset*Interval)) {
|
|
ScheduleData[i] = 0x00;
|
|
} else if ((i-(Offset*Interval))%(Interval * Stagger) == 0) {
|
|
ScheduleData[i] = 0x01;
|
|
} else {
|
|
ScheduleData[i] = 0x00;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Use the supplied schedule mask to turn off regions of the above schedule.
|
|
//
|
|
if (SchedMask != NULL) {
|
|
for (i=0 ; i<FRST_SIZE_OF_SCHEDULE_GRID ; ++i) {
|
|
ScheduleData[i] &= ~SchedMask[i];
|
|
}
|
|
}
|
|
|
|
//
|
|
// Apply the supplied override schedule to regions of the above schedule.
|
|
//
|
|
if (SchedOverride != NULL) {
|
|
for (i=0 ; i<FRST_SIZE_OF_SCHEDULE_GRID ; ++i) {
|
|
ScheduleData[i] |= SchedOverride[i];
|
|
}
|
|
}
|
|
|
|
memcpy((*ppSchedule)+sizeof(SCHEDULE),ScheduleData, FRST_SIZE_OF_SCHEDULE_GRID);
|
|
return Status;
|
|
}
|
|
|
|
|
|
#if 0
|
|
DWORD
|
|
DeleteSubscription(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName,
|
|
PWCHAR MemberName,
|
|
PWCHAR ComputerDn
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Delete a Subscription.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
PWCHAR MemberDn = NULL;
|
|
PWCHAR SubscriptionDn = NULL;
|
|
PWCHAR MemberAttrs[3];
|
|
PWCHAR SubscriptionAttrs[3];
|
|
PWCHAR SearchFilter = NULL;
|
|
DWORD NoOfMembers;
|
|
DWORD NoOfSubscriptions;
|
|
PWCHAR LocalComputerDn = NULL;
|
|
PLDAPMessage LdapEntry = NULL;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
|
|
|
|
MK_ATTRS_2(MemberAttrs, ATTR_DN, ATTR_COMPUTER_REF);
|
|
MK_ATTRS_2(SubscriptionAttrs, ATTR_DN, ************ ATTR_MEMBER_REF);
|
|
|
|
//
|
|
// We need to get to the member object and the computer object.
|
|
// To get to the member object we need the member name or the computer dn.
|
|
// To get to the computer object we need a member with valid computerref
|
|
// or the computerdn parameter.
|
|
//
|
|
if ((MemberName == NULL) && (ComputerDn == NULL)) {
|
|
printf("ERROR: MemberName and ComputerDn are NULL in DeleteSubscription.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
return Status;
|
|
}
|
|
|
|
SearchFilter = (PWCHAR) malloc((max((ComputerDn != NULL)?wcslen(ComputerDn):0,
|
|
(MemberDn != NULL)?wcslen(MemberDn):0)
|
|
+ MAX_PATH)
|
|
* sizeof(WCHAR));
|
|
|
|
ReplicaSetDn = ExtendDn(NTFRSSettingsDn, ReplicaSetName);
|
|
|
|
if (MemberName != NULL) {
|
|
MemberDn = ExtendDn(ReplicaSetDn, MemberName);
|
|
} else {
|
|
//
|
|
// Use computerdn to get the memberdn.
|
|
//
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_COMPUTER_REF, L"=", ComputerDn, L")" , CLASS_MEMBER, L")");
|
|
|
|
if (!LdapSearchInit(pLdap, ReplicaSetDn, LDAP_SCOPE_ONELEVEL, SearchFilter,
|
|
MemberAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIPTION_DELETE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfMembers == 0) {
|
|
printf("Error deleting Subscription; member not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_NOT_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfMembers > 1) {
|
|
printf("Error deleting Subscription; duplicate members found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_DUPS_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
MemberDn = FindValue(LdapEntry, ATTR_DN);
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
}
|
|
|
|
DPRINT1("MemberDn:%ws\n", MemberDn);
|
|
|
|
if (ComputerDn == NULL) {
|
|
//
|
|
// Use MemberDn to find the computerDn. We will come here
|
|
// only if MemberName is supplied but computerdn is not.
|
|
//
|
|
if (!LdapSearchInit(pLdap, MemberDn, LDAP_SCOPE_BASE, NULL,
|
|
MemberAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIPTION_DELETE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfMembers == 0) {
|
|
printf("Error deleting Subscription; member not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_NOT_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
LocalComputerDn = FindValue(LdapEntry, ATTR_COMPUTER_REF);
|
|
if (LocalComputerDn == NULL) {
|
|
printf("Error deleting Subscription; computerdn not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_NOT_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
} else {
|
|
LocalComputerDn = FrsWcsDup(ComputerDn);
|
|
}
|
|
|
|
|
|
//
|
|
// We have the computerdn and the memberdn. Now check if a Subscription
|
|
// already exists.
|
|
//
|
|
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_MEMBER_REF, L"=", MemberDn, L")" , CLASS_SUBSCRIPTION, L")");
|
|
|
|
if (!LdapSearchInit(pLdap, LocalComputerDn, LDAP_SCOPE_SUBTREE, SearchFilter,
|
|
SubscriptionAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIPTION_DELETE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfSubscriptions = FrsSearchContext.EntriesInPage;
|
|
|
|
if ((NoOfSubscriptions > 1) && (bAffectAll != TRUE)) {
|
|
printf("Duplicate Subscriptions found. Deleting all.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_DUPS_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfSubscriptions != 0) {
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
SubscriptionDn = FindValue(LdapEntry, ATTR_DN);
|
|
DPRINT1("Deleting Dn:%ws\n", SubscriptionDn);
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_delete_s(pLdap, %ws);\n", SubscriptionDn);
|
|
} else {
|
|
LStatus = ldap_delete_s(pLdap, SubscriptionDn);
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
printf("ERROR - Can't delete %ws: %ws\n",
|
|
SubscriptionDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIPTION_DELETE_FAILED;
|
|
}
|
|
}
|
|
FREE(SubscriptionDn);
|
|
}
|
|
} else {
|
|
printf("Warning deleting; Subscription not found.\n");
|
|
Status = MKDSOE_SUBSCRIPTION_NOT_FOUND_DELETE;
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
|
|
CLEANUP:
|
|
|
|
FREE(SearchFilter);
|
|
FREE(ReplicaSetDn);
|
|
FREE(MemberDn);
|
|
FREE(LocalComputerDn);
|
|
FREE(SubscriptionDn);
|
|
FreeMod(&Mod);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
UpdateSubscription(
|
|
PWCHAR SubscriptionDn,
|
|
PWCHAR MemberRef,
|
|
PWCHAR RootPath,
|
|
PWCHAR StagePath
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Update the Subscription.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR Attrs[5];
|
|
PLDAPMessage LdapEntry = NULL;
|
|
PWCHAR CurMemberRef = NULL;
|
|
PWCHAR CurRootPath = NULL;
|
|
PWCHAR CurStagePath = NULL;
|
|
DWORD NoOfSubscriptions;
|
|
BOOL bNeedsUpdate = FALSE;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
|
|
MK_ATTRS_4(Attrs, ATTR_DN, ATTR_MEMBER_REF, ATTR_REPLICA_ROOT, ATTR_REPLICA_STAGE);
|
|
|
|
if (!LdapSearchInit(pLdap, SubscriptionDn, LDAP_SCOPE_BASE, NULL,
|
|
Attrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_UPDATE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfSubscriptions = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfSubscriptions == 0) {
|
|
printf("Error updating; Subscription not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_NOT_FOUND_UPDATE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
CurMemberRef = FindValue(LdapEntry, ATTR_MEMBER_REF);
|
|
CurRootPath = FindValue(LdapEntry, ATTR_REPLICA_ROOT);
|
|
CurStagePath = FindValue(LdapEntry, ATTR_REPLICA_STAGE);
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
DPRINT1("SubscriptionDn:%ws\n", SubscriptionDn);
|
|
|
|
// Check ATTR_MEMBER_REF
|
|
// if a ref is supplied then make sure it is same as the current ref.
|
|
// if a ref is not supplied then leave it as it is.
|
|
if (MemberRef != NULL) {
|
|
if ((CurMemberRef == NULL) ||
|
|
((CurMemberRef != NULL) && wcscmp(MemberRef, CurMemberRef))) {
|
|
AddMod(ATTR_MEMBER_REF, MemberRef, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsMemberReference:%ws\n", MemberRef);
|
|
}
|
|
}
|
|
|
|
// Check ATTR_REPLICA_ROOT
|
|
// if a path is supplied then make sure it is same as the current path.
|
|
// if a path is not supplied then leave it as it is.
|
|
if (RootPath != NULL) {
|
|
if ((CurRootPath == NULL) ||
|
|
((CurRootPath != NULL) && wcscmp(RootPath, CurRootPath))) {
|
|
AddMod(ATTR_REPLICA_ROOT, RootPath, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsRootPath:%ws\n", RootPath);
|
|
}
|
|
}
|
|
|
|
// Check ATTR_REPLICA_STAGE
|
|
// if a path is supplied then make sure it is same as the current path.
|
|
// if a path is not supplied then leave it as it is.
|
|
if (StagePath != NULL) {
|
|
if ((CurStagePath == NULL) ||
|
|
((CurStagePath != NULL) && wcscmp(StagePath, CurStagePath))) {
|
|
AddMod(ATTR_REPLICA_STAGE, StagePath, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsRootPath:%ws\n", StagePath);
|
|
}
|
|
}
|
|
|
|
|
|
if (bNeedsUpdate) {
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_modify_s(pLdap, %ws, Mod);\n", SubscriptionDn);
|
|
} else {
|
|
LStatus = ldap_modify_s(pLdap, SubscriptionDn, Mod);
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
printf("ERROR - Can't update %ws: %ws\n",
|
|
SubscriptionDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_UPDATE_FAILED;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
printf("No update required\n");
|
|
}
|
|
|
|
CLEANUP:
|
|
|
|
FREE(CurMemberRef);
|
|
FREE(CurRootPath);
|
|
FREE(CurStagePath);
|
|
FreeMod(&Mod);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
CreateNewSubscription(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName,
|
|
PWCHAR MemberName,
|
|
PWCHAR ComputerDn,
|
|
PWCHAR RootPath,
|
|
PWCHAR StagePath,
|
|
PWCHAR RefDCName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Create a new Subscription.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
PWCHAR MemberDn = NULL;
|
|
PWCHAR SubscriptionDn = NULL;
|
|
PWCHAR MemberAttrs[3];
|
|
PWCHAR SubscriptionAttrs[3];
|
|
PWCHAR SearchFilter = NULL;
|
|
DWORD NoOfMembers;
|
|
DWORD NoOfSubscriptions;
|
|
PWCHAR LocalComputerDn = NULL;
|
|
PLDAPMessage LdapEntry = NULL;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
|
|
|
|
MK_ATTRS_2(MemberAttrs, ATTR_DN, ATTR_COMPUTER_REF);
|
|
MK_ATTRS_2(SubscriptionAttrs, ************* ATTR_DN, ATTR_MEMBER_REF);
|
|
|
|
//
|
|
// We need to get to the member object and the computer object.
|
|
// To get to the member object we need the member name or the computer dn.
|
|
//
|
|
// To get to the computer object we need a member with valid computerref
|
|
// or the computerdn parameter.
|
|
//
|
|
if ((MemberName == NULL) && (ComputerDn == NULL)) {
|
|
Status = MKDSOE_BAD_ARG;
|
|
return Status;
|
|
}
|
|
|
|
SearchFilter = (PWCHAR) malloc((max((ComputerDn != NULL)?wcslen(ComputerDn):0,
|
|
(MemberDn != NULL)?wcslen(MemberDn):0)
|
|
+ MAX_PATH)
|
|
* sizeof(WCHAR));
|
|
|
|
ReplicaSetDn = ExtendDn(NTFRSSettingsDn, ReplicaSetName);
|
|
|
|
if (MemberName != NULL) {
|
|
MemberDn = ExtendDn(ReplicaSetDn, MemberName);
|
|
} else {
|
|
//
|
|
// Use computerdn to get the memberdn.
|
|
//
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_COMPUTER_REF, L"=", ComputerDn, L")" , CLASS_MEMBER, L")");
|
|
|
|
if (!LdapSearchInit(pLdap, ReplicaSetDn, LDAP_SCOPE_ONELEVEL, SearchFilter,
|
|
MemberAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfMembers == 0) {
|
|
printf("Error creating Subscription; member not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfMembers > 1) {
|
|
printf("Error creating Subscription; duplicate members found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_DUPS_FOUND_UPDATE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
MemberDn = FindValue(LdapEntry, ATTR_DN);
|
|
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
}
|
|
|
|
DPRINT1("MemberDn:%ws\n", MemberDn);
|
|
|
|
if (ComputerDn == NULL) {
|
|
//
|
|
// Use MemberDn to find the computerDn. We will come here
|
|
// only if MemberName is supplied but computerdn is not.
|
|
//
|
|
if (!LdapSearchInit(pLdap, MemberDn, LDAP_SCOPE_BASE, NULL,
|
|
MemberAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfMembers == 0) {
|
|
printf("Error creating Subscription; member not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
LocalComputerDn = FindValue(LdapEntry, ATTR_COMPUTER_REF);
|
|
if (LocalComputerDn == NULL) {
|
|
printf("Error creating Subscription; computerdn not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
} else {
|
|
LocalComputerDn = FrsWcsDup(ComputerDn);
|
|
}
|
|
|
|
|
|
//
|
|
// We have the computerdn and the memberdn. Now check if a Subscription
|
|
// already exists.
|
|
//
|
|
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_MEMBER_REF, L"=", MemberDn, L")" , CLASS_SUBSCRIPTION, L")");
|
|
|
|
if (!LdapSearchInit(pLdap, LocalComputerDn, LDAP_SCOPE_SUBTREE, SearchFilter,
|
|
SubscriptionAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfSubscriptions = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfSubscriptions > 1) {
|
|
printf("Error creating Subscription; duplicate found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIPTION_DUPS_FOUND_UPDATE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfSubscriptions == 1) {
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
SubscriptionDn = FindValue(LdapEntry, ATTR_DN);
|
|
}
|
|
Status = UpdateSubscription(SubscriptionDn, MemberDn, RootPath, StagePath);
|
|
} else {
|
|
SubscriptionDn = ExtendDn(LocalComputerDn, MKDSOE_SUBSCRIPTION);
|
|
|
|
// ATTR_DN
|
|
DPRINT1("SubscriptionDn:%ws\n", SubscriptionDn);
|
|
AddMod(ATTR_CLASS, ATTR_SUBSCRIPTIONS, &Mod);
|
|
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", SubscriptionDn);
|
|
} else {
|
|
LStatus = ldap_add_s(pLdap, SubscriptionDn, Mod);
|
|
|
|
if ((LStatus != LDAP_ALREADY_EXISTS) && (LStatus != LDAP_SUCCESS)) {
|
|
|
|
printf("ERROR - Can't create %ws: %ws\n",
|
|
SubscriptionDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
FreeMod(&Mod);
|
|
************ check
|
|
SubscriptionDn = ExtendDn(SubscriptionDn, ReplicaSetName);
|
|
|
|
// ATTR_DN
|
|
DPRINT1("SubscriptionDn:%ws\n", SubscriptionDn);
|
|
AddMod(ATTR_CLASS, ATTR_SUBSCRIPTION, &Mod);
|
|
|
|
// ATTR_MEMBER_REF
|
|
AddMod(ATTR_MEMBER_REF, MemberDn, &Mod);
|
|
DPRINT1(" FrsMemberReference:%ws\n", MemberDn);
|
|
|
|
// ATTR_REPLICA_ROOT
|
|
AddMod(ATTR_REPLICA_ROOT, RootPath, &Mod);
|
|
DPRINT1(" FrsRootPath:%ws\n", RootPath);
|
|
|
|
// ATTR_REPLICA_STAGE
|
|
AddMod(ATTR_REPLICA_STAGE, StagePath, &Mod);
|
|
DPRINT1(" FrsStagePath:%ws\n", StagePath);
|
|
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", SubscriptionDn);
|
|
} else {
|
|
LStatus = ldap_add_s(pLdap, SubscriptionDn, Mod);
|
|
|
|
if ((LStatus == LDAP_CONSTRAINT_VIOLATION) && RefDCName != NULL) {
|
|
//
|
|
// prepare the server hint. Needed in case the member object
|
|
// is just created on another DC than the one on which the
|
|
// Subscription is being created.
|
|
//
|
|
LDAPControl simpleControl;
|
|
PLDAPControl controlArray[2];
|
|
INT rc;
|
|
BERVAL* pBerVal = NULL;
|
|
BerElement* pBer;
|
|
|
|
pBer = ber_alloc_t(LBER_USE_DER);
|
|
if (!pBer)
|
|
{
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
DPRINT1("Sending binding DC Name %ws\n",RefDCName);
|
|
rc = ber_printf(pBer,"{io}", 0, RefDCName, wcslen(RefDCName) * sizeof(WCHAR));
|
|
if ( rc == -1 ) {
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
rc = ber_flatten(pBer, &pBerVal);
|
|
if (rc == -1)
|
|
{
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
ber_free(pBer,1);
|
|
|
|
controlArray[0] = &simpleControl;
|
|
controlArray[1] = NULL;
|
|
|
|
// simpleControl.ldctl_oid = LDAP_SERVER_GC_VERIFY_NAME_OID_W;
|
|
simpleControl.ldctl_oid = LDAP_SERVER_VERIFY_NAME_OID_W;
|
|
simpleControl.ldctl_iscritical = TRUE;
|
|
simpleControl.ldctl_value = *pBerVal;
|
|
|
|
LStatus = ldap_add_ext_s(pLdap,
|
|
SubscriptionDn,
|
|
Mod,
|
|
(PLDAPControl *)&controlArray, //ServerControls,
|
|
NULL //ClientControls,
|
|
);
|
|
}
|
|
|
|
if (LStatus == LDAP_ALREADY_EXISTS) {
|
|
//
|
|
// If the object already exists then convert the create to an update.
|
|
// This is to allow the user to run the data file with creates twice without
|
|
// generating errors but only fixing the objects that have changed.
|
|
//
|
|
Status = UpdateSubscription(SubscriptionDn, MemberDn, RootPath, StagePath);
|
|
|
|
} else if (LStatus != LDAP_SUCCESS) {
|
|
DPRINT2("ERROR - Can't create %ws: %ws\n",
|
|
SubscriptionDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIPTION_OBJ_CRE_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
|
|
CLEANUP:
|
|
|
|
FreeMod(&Mod);
|
|
FREE(ReplicaSetDn);
|
|
FREE(MemberDn);
|
|
FREE(SubscriptionDn);
|
|
FREE(SearchFilter);
|
|
FREE(LocalComputerDn);
|
|
return Status;
|
|
}
|
|
#endif 0
|
|
|
|
DWORD
|
|
DeleteSubscriber(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName,
|
|
PWCHAR MemberName,
|
|
PWCHAR ComputerDn
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Delete a subscriber.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
PWCHAR MemberDn = NULL;
|
|
PWCHAR SubscriberDn = NULL;
|
|
PWCHAR MemberAttrs[3];
|
|
PWCHAR SubscriberAttrs[3];
|
|
PWCHAR SearchFilter = NULL;
|
|
DWORD NoOfMembers;
|
|
DWORD NoOfSubscribers;
|
|
PWCHAR LocalComputerDn = NULL;
|
|
PLDAPMessage LdapEntry = NULL;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
|
|
|
|
MK_ATTRS_2(MemberAttrs, ATTR_DN, ATTR_COMPUTER_REF);
|
|
MK_ATTRS_2(SubscriberAttrs, ATTR_DN, ATTR_MEMBER_REF);
|
|
|
|
//
|
|
// We need to get to the member object and the computer object.
|
|
// To get to the member object we need the member name or the computer dn.
|
|
// To get to the computer object we need a member with valid computerref
|
|
// or the computerdn parameter.
|
|
//
|
|
if ((MemberName == NULL) && (ComputerDn == NULL)) {
|
|
printf("ERROR: MemberName and ComputerDn are NULL in DeleteSubscriber.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
return Status;
|
|
}
|
|
|
|
SearchFilter = (PWCHAR) malloc((max((ComputerDn != NULL)?wcslen(ComputerDn):0,
|
|
(MemberDn != NULL)?wcslen(MemberDn):0)
|
|
+ MAX_PATH)
|
|
* sizeof(WCHAR));
|
|
|
|
ReplicaSetDn = ExtendDn(NTFRSSettingsDn, ReplicaSetName);
|
|
|
|
if (MemberName != NULL) {
|
|
MemberDn = ExtendDn(ReplicaSetDn, MemberName);
|
|
} else {
|
|
//
|
|
// Use computerdn to get the memberdn.
|
|
//
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_COMPUTER_REF, L"=", ComputerDn, L")" , CLASS_MEMBER, L")");
|
|
|
|
if (!LdapSearchInit(pLdap, ReplicaSetDn, LDAP_SCOPE_ONELEVEL, SearchFilter,
|
|
MemberAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIBER_DELETE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfMembers == 0) {
|
|
printf("Error deleting subscriber; member not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_NOT_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfMembers > 1) {
|
|
printf("Error deleting subscriber; duplicate members found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_DUPS_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
MemberDn = FindValue(LdapEntry, ATTR_DN);
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
}
|
|
|
|
DPRINT1("MemberDn:%ws\n", MemberDn);
|
|
|
|
if (ComputerDn == NULL) {
|
|
//
|
|
// Use MemberDn to find the computerDn. We will come here
|
|
// only if MemberName is supplied but computerdn is not.
|
|
//
|
|
if (!LdapSearchInit(pLdap, MemberDn, LDAP_SCOPE_BASE, NULL,
|
|
MemberAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIBER_DELETE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfMembers == 0) {
|
|
printf("Error deleting subscriber; member not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_NOT_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
LocalComputerDn = FindValue(LdapEntry, ATTR_COMPUTER_REF);
|
|
if (LocalComputerDn == NULL) {
|
|
printf("Error deleting subscriber; computerdn not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_NOT_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
} else {
|
|
LocalComputerDn = FrsWcsDup(ComputerDn);
|
|
}
|
|
|
|
|
|
//
|
|
// We have the computerdn and the memberdn. Now check if a subscriber
|
|
// already exists.
|
|
//
|
|
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_MEMBER_REF, L"=", MemberDn, L")" , CLASS_SUBSCRIBER, L")");
|
|
|
|
if (!LdapSearchInit(pLdap, LocalComputerDn, LDAP_SCOPE_SUBTREE, SearchFilter,
|
|
SubscriberAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIBER_DELETE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfSubscribers = FrsSearchContext.EntriesInPage;
|
|
|
|
if ((NoOfSubscribers > 1) && (bAffectAll != TRUE)) {
|
|
printf("Duplicate subscribers found. Deleting all.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_DUPS_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfSubscribers != 0) {
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
SubscriberDn = FindValue(LdapEntry, ATTR_DN);
|
|
DPRINT1("Deleting Dn:%ws\n", SubscriberDn);
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_delete_s(pLdap, %ws);\n", SubscriberDn);
|
|
} else {
|
|
LStatus = ldap_delete_s(pLdap, SubscriberDn);
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
printf("ERROR - Can't delete %ws: %ws\n",
|
|
SubscriberDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIBER_DELETE_FAILED;
|
|
}
|
|
}
|
|
FREE(SubscriberDn);
|
|
}
|
|
} else {
|
|
printf("Warning deleting; subscriber not found.\n");
|
|
Status = MKDSOE_SUBSCRIBER_NOT_FOUND_DELETE;
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
|
|
CLEANUP:
|
|
|
|
FREE(SearchFilter);
|
|
FREE(ReplicaSetDn);
|
|
FREE(MemberDn);
|
|
FREE(LocalComputerDn);
|
|
FREE(SubscriberDn);
|
|
FreeMod(&Mod);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
UpdateSubscriber(
|
|
PWCHAR SubscriberDn,
|
|
PWCHAR MemberRef,
|
|
PWCHAR RootPath,
|
|
PWCHAR StagePath
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Update the subscriber.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR Attrs[5];
|
|
PLDAPMessage LdapEntry = NULL;
|
|
PWCHAR CurMemberRef = NULL;
|
|
PWCHAR CurRootPath = NULL;
|
|
PWCHAR CurStagePath = NULL;
|
|
DWORD NoOfSubscribers;
|
|
BOOL bNeedsUpdate = FALSE;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
|
|
MK_ATTRS_4(Attrs, ATTR_DN, ATTR_MEMBER_REF, ATTR_REPLICA_ROOT, ATTR_REPLICA_STAGE);
|
|
|
|
if (!LdapSearchInit(pLdap, SubscriberDn, LDAP_SCOPE_BASE, NULL,
|
|
Attrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_UPDATE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfSubscribers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfSubscribers == 0) {
|
|
printf("Error updating; subscriber not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_NOT_FOUND_UPDATE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
CurMemberRef = FindValue(LdapEntry, ATTR_MEMBER_REF);
|
|
CurRootPath = FindValue(LdapEntry, ATTR_REPLICA_ROOT);
|
|
CurStagePath = FindValue(LdapEntry, ATTR_REPLICA_STAGE);
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
DPRINT1("SubscriberDn:%ws\n", SubscriberDn);
|
|
|
|
// Check ATTR_MEMBER_REF
|
|
// if a ref is supplied then make sure it is same as the current ref.
|
|
// if a ref is not supplied then leave it as it is.
|
|
if (MemberRef != NULL) {
|
|
if ((CurMemberRef == NULL) ||
|
|
((CurMemberRef != NULL) && wcscmp(MemberRef, CurMemberRef))) {
|
|
AddMod(ATTR_MEMBER_REF, MemberRef, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsMemberReference:%ws\n", MemberRef);
|
|
}
|
|
}
|
|
|
|
// Check ATTR_REPLICA_ROOT
|
|
// if a path is supplied then make sure it is same as the current path.
|
|
// if a path is not supplied then leave it as it is.
|
|
if (RootPath != NULL) {
|
|
if ((CurRootPath == NULL) ||
|
|
((CurRootPath != NULL) && wcscmp(RootPath, CurRootPath))) {
|
|
AddMod(ATTR_REPLICA_ROOT, RootPath, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsRootPath:%ws\n", RootPath);
|
|
}
|
|
}
|
|
|
|
// Check ATTR_REPLICA_STAGE
|
|
// if a path is supplied then make sure it is same as the current path.
|
|
// if a path is not supplied then leave it as it is.
|
|
if (StagePath != NULL) {
|
|
if ((CurStagePath == NULL) ||
|
|
((CurStagePath != NULL) && wcscmp(StagePath, CurStagePath))) {
|
|
AddMod(ATTR_REPLICA_STAGE, StagePath, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsStagePath:%ws\n", StagePath);
|
|
}
|
|
}
|
|
|
|
|
|
if (bNeedsUpdate) {
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_modify_s(pLdap, %ws, Mod);\n", SubscriberDn);
|
|
} else {
|
|
LStatus = ldap_modify_s(pLdap, SubscriberDn, Mod);
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
printf("ERROR - Can't update %ws: %ws\n",
|
|
SubscriberDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_UPDATE_FAILED;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
printf("No update required\n");
|
|
}
|
|
|
|
CLEANUP:
|
|
|
|
FREE(CurMemberRef);
|
|
FREE(CurRootPath);
|
|
FREE(CurStagePath);
|
|
FreeMod(&Mod);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
CreateNewSubscriber(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName,
|
|
PWCHAR MemberName,
|
|
PWCHAR ComputerDn,
|
|
PWCHAR RootPath,
|
|
PWCHAR StagePath,
|
|
PWCHAR WorkingPath,
|
|
PWCHAR RefDCName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Create a new subscriber.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
PWCHAR MemberDn = NULL;
|
|
PWCHAR SubscriberDn = NULL;
|
|
PWCHAR SubscriptionDn = NULL;
|
|
PWCHAR MemberAttrs[3];
|
|
PWCHAR SubscriberAttrs[3];
|
|
PWCHAR SearchFilter = NULL;
|
|
DWORD NoOfMembers;
|
|
DWORD NoOfSubscribers;
|
|
PWCHAR LocalComputerDn = NULL;
|
|
PLDAPMessage LdapEntry = NULL;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
PWCHAR SaveDn;
|
|
PWCHAR DFSRoot, DFSJunction;
|
|
DWORD Vbar;
|
|
|
|
if (bDFSNaming) {
|
|
Vbar = wcscspn(DFSName, L'|');
|
|
if ((Vbar == wcslen(DFSName) || (Vbar == 0)) {
|
|
//
|
|
// No vertical bar found.
|
|
//
|
|
Status = MKDSOE_BAD_ARG;
|
|
return Status;
|
|
}
|
|
//
|
|
// Extract the name components.
|
|
//
|
|
DFSRoot = FrsWcsDup(DFSName);
|
|
DFSRoot[Vbar] = L'\0';
|
|
DFSJunction = &DFSName[Vbar+1];
|
|
}
|
|
|
|
MK_ATTRS_2(MemberAttrs, ATTR_DN, ATTR_COMPUTER_REF);
|
|
MK_ATTRS_2(SubscriberAttrs, ATTR_DN, ATTR_MEMBER_REF);
|
|
|
|
//
|
|
// We need to get to the member object and the computer object.
|
|
// To get to the member object we need the member name or the computer dn.
|
|
//
|
|
// To get to the computer object we need a member with valid computerref
|
|
// or the computerdn parameter.
|
|
//
|
|
if ((MemberName == NULL) && (ComputerDn == NULL)) {
|
|
Status = MKDSOE_BAD_ARG;
|
|
return Status;
|
|
}
|
|
|
|
SearchFilter = (PWCHAR) malloc((max((ComputerDn != NULL)?wcslen(ComputerDn):0,
|
|
(MemberDn != NULL)?wcslen(MemberDn):0)
|
|
+ MAX_PATH)
|
|
* sizeof(WCHAR));
|
|
|
|
ReplicaSetDn = ExtendDn(NTFRSSettingsDn, ReplicaSetName);
|
|
|
|
if (MemberName != NULL) {
|
|
MemberDn = ExtendDn(ReplicaSetDn, MemberName);
|
|
} else {
|
|
//
|
|
// Use computerdn to get the memberdn.
|
|
//
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_COMPUTER_REF, L"=", ComputerDn, L")" , CLASS_MEMBER, L")");
|
|
|
|
if (!LdapSearchInit(pLdap, ReplicaSetDn, LDAP_SCOPE_ONELEVEL, SearchFilter,
|
|
MemberAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfMembers == 0) {
|
|
printf("Error creating subscriber; member not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfMembers > 1) {
|
|
printf("Error creating subscriber; duplicate members found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_DUPS_FOUND_UPDATE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
MemberDn = FindValue(LdapEntry, ATTR_DN);
|
|
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
}
|
|
|
|
DPRINT1("MemberDn:%ws\n", MemberDn);
|
|
|
|
if (ComputerDn == NULL) {
|
|
//
|
|
// Use MemberDn to find the computerDn. We will come here
|
|
// only if MemberName is supplied but computerdn is not.
|
|
//
|
|
if (!LdapSearchInit(pLdap, MemberDn, LDAP_SCOPE_BASE, NULL,
|
|
MemberAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfMembers == 0) {
|
|
printf("Error creating subscriber; member not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
LocalComputerDn = FindValue(LdapEntry, ATTR_COMPUTER_REF);
|
|
if (LocalComputerDn == NULL) {
|
|
printf("Error creating subscriber; computerdn not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
} else {
|
|
LocalComputerDn = FrsWcsDup(ComputerDn);
|
|
}
|
|
|
|
|
|
//
|
|
// We have the computerdn and the memberdn. Now check if a subscriber
|
|
// already exists.
|
|
//
|
|
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_MEMBER_REF, L"=", MemberDn, L")" , CLASS_SUBSCRIBER, L")");
|
|
|
|
if (!LdapSearchInit(pLdap, LocalComputerDn, LDAP_SCOPE_SUBTREE, SearchFilter,
|
|
SubscriberAttrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfSubscribers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfSubscribers > 1) {
|
|
printf("Error creating subscriber; duplicate found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SUBSCRIBER_DUPS_FOUND_UPDATE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfSubscribers == 1) {
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
SubscriberDn = FindValue(LdapEntry, ATTR_DN);
|
|
}
|
|
Status = UpdateSubscriber(SubscriberDn, MemberDn, RootPath, StagePath);
|
|
} else {
|
|
//
|
|
// Create the new subscriber.
|
|
//
|
|
|
|
SubscriptionDn = ExtendDn(LocalComputerDn, MKDSOE_SUBSCRIPTION);
|
|
|
|
// ATTR_DN
|
|
DPRINT1("SubscriptionDn:%ws\n", SubscriptionDn);
|
|
AddMod(ATTR_CLASS, ATTR_SUBSCRIPTIONS, &Mod);
|
|
|
|
if (WorkingPath != NULL) {
|
|
// ATTR_WORKING_PATH
|
|
AddMod(ATTR_REPLICA_STAGE, WorkingPath, &Mod);
|
|
DPRINT1(" FrsWorkingPath:%ws\n", WorkingPath);
|
|
} else {
|
|
fprintf(stderr, "WARNING: Creating subscription '%ws' with no working path\n", MKDSOE_SUBSCRIPTION);
|
|
}
|
|
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", SubscriptionDn);
|
|
} else {
|
|
LStatus = ldap_add_s(pLdap, SubscriptionDn, Mod);
|
|
|
|
if ((LStatus != LDAP_ALREADY_EXISTS) && (LStatus != LDAP_SUCCESS)) {
|
|
|
|
printf("ERROR - Can't create %ws: %ws\n",
|
|
SubscriptionDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
FreeMod(&Mod);
|
|
|
|
//
|
|
// If this member is to be added using the Win2K DFS naming convention
|
|
// then we need to create a few more subscription containers. E.G.
|
|
//
|
|
// (cn=software$|packages, frs subscriber (DFS junction)
|
|
// cn=software$, frs subscription (DFS Root)
|
|
// cn=56421f6a-9edd-4098-a101-bf2e161e4572, frs subscription (domain guid for DFS Guid)
|
|
// cn=dfs volumes, frs subscription
|
|
// cn=ntfrs subscriptions, frs subscription
|
|
// cn=dfs-srv-01, computer object
|
|
// ou=multifunction terminal servers,
|
|
// ou=domain controllers,
|
|
// dc=cps,
|
|
// dc=gov,
|
|
// dc=uk)
|
|
//
|
|
//
|
|
if (bDFSNaming) {
|
|
|
|
SaveDn = SubscriptionDn;
|
|
SubscriptionDn = ExtendDn(SubscriptionDn, L"DFS VOLUMES");
|
|
FREE(SaveDn);
|
|
|
|
// ATTR_DN
|
|
DPRINT1("SubscriptionDn:%ws\n", SubscriptionDn);
|
|
AddMod(ATTR_CLASS, ATTR_SUBSCRIPTIONS, &Mod);
|
|
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", SubscriptionDn);
|
|
} else {
|
|
LStatus = ldap_add_s(pLdap, SubscriptionDn, Mod);
|
|
|
|
if ((LStatus != LDAP_ALREADY_EXISTS) && (LStatus != LDAP_SUCCESS)) {
|
|
printf("ERROR - Can't create %ws: %ws\n", SubscriptionDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
FreeMod(&Mod);
|
|
|
|
//
|
|
// Add subscription container with name of Domain Guid.
|
|
//
|
|
SaveDn = SubscriptionDn;
|
|
SubscriptionDn = ExtendDn(SubscriptionDn, DomainGuidStr);
|
|
FREE(SaveDn);
|
|
|
|
// ATTR_DN
|
|
DPRINT1("SubscriptionDn:%ws\n", SubscriptionDn);
|
|
AddMod(ATTR_CLASS, ATTR_SUBSCRIPTIONS, &Mod);
|
|
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", SubscriptionDn);
|
|
} else {
|
|
LStatus = ldap_add_s(pLdap, SubscriptionDn, Mod);
|
|
|
|
if ((LStatus != LDAP_ALREADY_EXISTS) && (LStatus != LDAP_SUCCESS)) {
|
|
printf("ERROR - Can't create %ws: %ws\n", SubscriptionDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
FreeMod(&Mod);
|
|
|
|
if (wcslen(DFSRoot) > 0) {
|
|
//
|
|
// Add subscription container with name of DFS Root.
|
|
//
|
|
SaveDn = SubscriptionDn;
|
|
SubscriptionDn = ExtendDn(SubscriptionDn, DFSRoot);
|
|
FREE(SaveDn);
|
|
|
|
// ATTR_DN
|
|
DPRINT1("SubscriptionDn:%ws\n", SubscriptionDn);
|
|
AddMod(ATTR_CLASS, ATTR_SUBSCRIPTIONS, &Mod);
|
|
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", SubscriptionDn);
|
|
} else {
|
|
LStatus = ldap_add_s(pLdap, SubscriptionDn, Mod);
|
|
|
|
if ((LStatus != LDAP_ALREADY_EXISTS) && (LStatus != LDAP_SUCCESS)) {
|
|
printf("ERROR - Can't create %ws: %ws\n", SubscriptionDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
FreeMod(&Mod);
|
|
|
|
if (wcslen(DFSJunction) > 0) {
|
|
//
|
|
// Add subscription container with name of DFS Junction.
|
|
//
|
|
SaveDn = SubscriptionDn;
|
|
SubscriptionDn = ExtendDn(SubscriptionDn, DFSJunction);
|
|
FREE(SaveDn);
|
|
|
|
// ATTR_DN
|
|
DPRINT1("SubscriptionDn:%ws\n", SubscriptionDn);
|
|
AddMod(ATTR_CLASS, ATTR_SUBSCRIPTIONS, &Mod);
|
|
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", SubscriptionDn);
|
|
} else {
|
|
LStatus = ldap_add_s(pLdap, SubscriptionDn, Mod);
|
|
|
|
if ((LStatus != LDAP_ALREADY_EXISTS) && (LStatus != LDAP_SUCCESS)) {
|
|
printf("ERROR - Can't create %ws: %ws\n", SubscriptionDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
FreeMod(&Mod);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now construct the subscriber object.
|
|
//
|
|
SubscriberDn = ExtendDn(SubscriptionDn, ReplicaSetName);
|
|
|
|
// ATTR_DN
|
|
DPRINT1("SubscriberDn:%ws\n", SubscriberDn);
|
|
AddMod(ATTR_CLASS, ATTR_SUBSCRIBER, &Mod);
|
|
|
|
// ATTR_MEMBER_REF
|
|
AddMod(ATTR_MEMBER_REF, MemberDn, &Mod);
|
|
DPRINT1(" FrsMemberReference:%ws\n", MemberDn);
|
|
|
|
// ATTR_REPLICA_ROOT
|
|
AddMod(ATTR_REPLICA_ROOT, RootPath, &Mod);
|
|
DPRINT1(" FrsRootPath:%ws\n", RootPath);
|
|
|
|
// ATTR_REPLICA_STAGE
|
|
AddMod(ATTR_REPLICA_STAGE, StagePath, &Mod);
|
|
DPRINT1(" FrsStagePath:%ws\n", StagePath);
|
|
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", SubscriberDn);
|
|
} else {
|
|
LStatus = ldap_add_s(pLdap, SubscriberDn, Mod);
|
|
|
|
if ((LStatus == LDAP_CONSTRAINT_VIOLATION) && RefDCName != NULL) {
|
|
//
|
|
// prepare the server hint. Needed in case the member object
|
|
// is just created on another DC than the one on which the
|
|
// subscriber is being created.
|
|
//
|
|
LDAPControl simpleControl;
|
|
PLDAPControl controlArray[2];
|
|
INT rc;
|
|
BERVAL* pBerVal = NULL;
|
|
BerElement* pBer;
|
|
|
|
pBer = ber_alloc_t(LBER_USE_DER);
|
|
if (!pBer)
|
|
{
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
DPRINT1("Sending binding DC Name %ws\n",RefDCName);
|
|
rc = ber_printf(pBer,"{io}", 0, RefDCName, wcslen(RefDCName) * sizeof(WCHAR));
|
|
if ( rc == -1 ) {
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
rc = ber_flatten(pBer, &pBerVal);
|
|
if (rc == -1)
|
|
{
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
ber_free(pBer,1);
|
|
|
|
controlArray[0] = &simpleControl;
|
|
controlArray[1] = NULL;
|
|
|
|
// simpleControl.ldctl_oid = LDAP_SERVER_GC_VERIFY_NAME_OID_W;
|
|
simpleControl.ldctl_oid = LDAP_SERVER_VERIFY_NAME_OID_W;
|
|
simpleControl.ldctl_iscritical = TRUE;
|
|
simpleControl.ldctl_value = *pBerVal;
|
|
|
|
LStatus = ldap_add_ext_s(pLdap,
|
|
SubscriberDn,
|
|
Mod,
|
|
(PLDAPControl *)&controlArray, //ServerControls,
|
|
NULL //ClientControls,
|
|
);
|
|
}
|
|
|
|
if (LStatus == LDAP_ALREADY_EXISTS) {
|
|
//
|
|
// If the object already exists then convert the create to an update.
|
|
// This is to allow the user to run the data file with creates twice without
|
|
// generating errors but only fixing the objects that have changed.
|
|
//
|
|
Status = UpdateSubscriber(SubscriberDn, MemberDn, RootPath, StagePath);
|
|
|
|
} else if (LStatus != LDAP_SUCCESS) {
|
|
DPRINT2("ERROR - Can't create %ws: %ws\n",
|
|
SubscriberDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
|
|
CLEANUP:
|
|
|
|
FreeMod(&Mod);
|
|
FREE(ReplicaSetDn);
|
|
FREE(MemberDn);
|
|
FREE(SubscriberDn);
|
|
FREE(SubscriptionDn);
|
|
FREE(SearchFilter);
|
|
FREE(LocalComputerDn);
|
|
FREE(DFSRoot);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
DeleteReplicaMember(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName,
|
|
PWCHAR MemberName,
|
|
PWCHAR ComputerDn
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Delete the replica member.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
PWCHAR MemberDn = NULL;
|
|
PWCHAR MemberCn = NULL;
|
|
PWCHAR ComputerRef = NULL;
|
|
PWCHAR Attrs[5];
|
|
PLDAPMessage LdapEntry = NULL;
|
|
PWCHAR SearchFilter = NULL;
|
|
DWORD NoOfMembers;
|
|
BOOL bNeedsUpdate = FALSE;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
|
|
MK_ATTRS_4(Attrs, ATTR_DN, ATTR_CN, ATTR_COMPUTER_REF, ATTR_SERVER_REF);
|
|
|
|
SearchFilter = (PWCHAR) malloc((((ComputerDn != NULL)?wcslen(ComputerDn):0)
|
|
+ MAX_PATH)
|
|
* sizeof(WCHAR));
|
|
|
|
ReplicaSetDn = ExtendDn(NTFRSSettingsDn, ReplicaSetName);
|
|
|
|
if (MemberName != NULL) {
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_CN, L"=", MemberName, L")" , CLASS_MEMBER, L")");
|
|
} else if (ComputerDn != NULL){
|
|
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_COMPUTER_REF, L"=", ComputerDn, L")" , CLASS_MEMBER, L")");
|
|
} else {
|
|
wcscpy(SearchFilter, CLASS_MEMBER);
|
|
}
|
|
|
|
if (!LdapSearchInit(pLdap, ReplicaSetDn, LDAP_SCOPE_ONELEVEL, SearchFilter,
|
|
Attrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_MEMBER_DELETE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if ((NoOfMembers > 1) && (bAffectAll != TRUE)) {
|
|
DPRINT0("Duplicate members found. Deleting all.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_MEMBER_DUPS_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfMembers != 0) {
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
MemberDn = FindValue(LdapEntry, ATTR_DN);
|
|
MemberCn = FindValue(LdapEntry, ATTR_CN);
|
|
ComputerRef = FindValue(LdapEntry, ATTR_COMPUTER_REF);
|
|
|
|
//
|
|
// If asked to delete the corresponding subscriber then do that first.
|
|
//
|
|
if (bDelSubscriber && ((ComputerRef != NULL) || (ComputerDn != NULL))) {
|
|
Status = DeleteSubscriber(NTFRSSettingsDn, ReplicaSetName,
|
|
MemberCn,
|
|
(ComputerRef != NULL)?ComputerRef:ComputerDn);
|
|
if (Status != MKDSOE_SUCCESS) {
|
|
LdapSearchClose(&FrsSearchContext);
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
DPRINT1("Deleting Dn:%ws\n", MemberDn);
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_delete_s(pLdap, %ws);\n", MemberDn);
|
|
} else {
|
|
LStatus = ldap_delete_s(pLdap, MemberDn);
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
DPRINT2("ERROR - Can't delete %ws: %ws\n",
|
|
MemberDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_MEMBER_DELETE_FAILED;
|
|
}
|
|
}
|
|
FREE(MemberDn);
|
|
FREE(MemberCn);
|
|
FREE(ComputerRef);
|
|
}
|
|
} else {
|
|
DPRINT0("Warning deleting; member not found.\n");
|
|
Status = MKDSOE_MEMBER_NOT_FOUND_DELETE;
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
CLEANUP:
|
|
|
|
FreeMod(&Mod);
|
|
FREE(ReplicaSetDn);
|
|
FREE(MemberDn);
|
|
FREE(MemberCn);
|
|
FREE(ComputerRef);
|
|
FREE(SearchFilter);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
UpdateReplicaMember(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName,
|
|
PWCHAR MemberName,
|
|
PWCHAR ComputerDn,
|
|
PWCHAR ServerRef,
|
|
PWCHAR RefDCName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Update the replica member parameters.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
PWCHAR MemberDn = NULL;
|
|
PWCHAR Attrs[4];
|
|
PLDAPMessage LdapEntry = NULL;
|
|
PWCHAR SearchFilter = NULL;
|
|
PWCHAR CurComputerRef = NULL;
|
|
PWCHAR CurServerRef = NULL;
|
|
DWORD NoOfMembers;
|
|
BOOL bNeedsUpdate = FALSE;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
|
|
|
|
MK_ATTRS_3(Attrs, ATTR_DN, ATTR_COMPUTER_REF, ATTR_SERVER_REF);
|
|
|
|
SearchFilter = (PWCHAR) malloc((((ComputerDn != NULL)?wcslen(ComputerDn):0)
|
|
+ MAX_PATH)
|
|
* sizeof(WCHAR));
|
|
|
|
ReplicaSetDn = ExtendDn(NTFRSSettingsDn, ReplicaSetName);
|
|
|
|
if (MemberName != NULL) {
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_CN, L"=", MemberName, L")" , CLASS_MEMBER, L")");
|
|
} else {
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_COMPUTER_REF, L"=", ComputerDn, L")" , CLASS_MEMBER, L")");
|
|
}
|
|
|
|
if (!LdapSearchInit(pLdap, ReplicaSetDn, LDAP_SCOPE_ONELEVEL, SearchFilter,
|
|
Attrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_MEMBER_OBJ_UPDATE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfMembers == 0) {
|
|
DPRINT0("Error updating; member not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_MEMBER_NOT_FOUND_UPDATE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfMembers > 1) {
|
|
DPRINT0("Error updating; duplicate members found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_MEMBER_DUPS_FOUND_UPDATE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
MemberDn = FindValue(LdapEntry, ATTR_DN);
|
|
CurComputerRef = FindValue(LdapEntry, ATTR_COMPUTER_REF);
|
|
CurServerRef = FindValue(LdapEntry, ATTR_SERVER_REF);
|
|
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
DPRINT1("MemberDn:%ws\n", MemberDn);
|
|
|
|
// Check ATTR_COMPUTER_REF
|
|
// if a ref is supplied then make sure it is same as the current ref.
|
|
// if a ref is not supplied then leave it as it is.
|
|
if (ComputerDn != NULL) {
|
|
if ((CurComputerRef == NULL) ||
|
|
((CurComputerRef != NULL) && wcscmp(ComputerDn, CurComputerRef))) {
|
|
AddMod(ATTR_COMPUTER_REF, ComputerDn, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsComputerReference:%ws\n", ComputerDn);
|
|
}
|
|
}
|
|
|
|
// Check ATTR_SERVER_REF
|
|
// if a ref is supplied then make sure it is same as the current ref.
|
|
// if a ref is not supplied then leave it as it is.
|
|
if (ServerRef != NULL) {
|
|
if ((CurServerRef == NULL) ||
|
|
((CurServerRef != NULL) && wcscmp(ServerRef, CurServerRef))) {
|
|
AddMod(ATTR_SERVER_REF, ServerRef, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New ServerReference:%ws\n", ServerRef);
|
|
}
|
|
}
|
|
|
|
|
|
if (bNeedsUpdate) {
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_modify_s(pLdap, %ws, Mod);\n", MemberDn);
|
|
} else {
|
|
LStatus = ldap_modify_s(pLdap, MemberDn, Mod);
|
|
|
|
if ((LStatus == LDAP_CONSTRAINT_VIOLATION) && RefDCName != NULL) {
|
|
//
|
|
// prepare the server hint. Needed in case the member object
|
|
// is just created on another DC than the one on which the
|
|
// subscriber is being created.
|
|
//
|
|
LDAPControl simpleControl;
|
|
PLDAPControl controlArray[2];
|
|
INT rc;
|
|
BERVAL* pBerVal = NULL;
|
|
BerElement* pBer;
|
|
|
|
pBer = ber_alloc_t(LBER_USE_DER);
|
|
if (!pBer)
|
|
{
|
|
Status = MKDSOE_MEMBER_OBJ_UPDATE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
DPRINT1("Sending binding DC Name %ws\n",RefDCName);
|
|
rc = ber_printf(pBer,"{io}", 0, RefDCName, wcslen(RefDCName) * sizeof(WCHAR));
|
|
if ( rc == -1 ) {
|
|
Status = MKDSOE_MEMBER_OBJ_UPDATE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
rc = ber_flatten(pBer, &pBerVal);
|
|
if (rc == -1)
|
|
{
|
|
Status = MKDSOE_MEMBER_OBJ_UPDATE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
ber_free(pBer,1);
|
|
|
|
controlArray[0] = &simpleControl;
|
|
controlArray[1] = NULL;
|
|
|
|
// simpleControl.ldctl_oid = LDAP_SERVER_GC_VERIFY_NAME_OID_W;
|
|
simpleControl.ldctl_oid = LDAP_SERVER_VERIFY_NAME_OID_W;
|
|
simpleControl.ldctl_iscritical = TRUE;
|
|
simpleControl.ldctl_value = *pBerVal;
|
|
|
|
LStatus = ldap_modify_ext_s(pLdap,
|
|
MemberDn,
|
|
Mod,
|
|
(PLDAPControl *)&controlArray, //ServerControls,
|
|
NULL //ClientControls,
|
|
);
|
|
}
|
|
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
DPRINT2("ERROR - Can't update %ws: %ws\n",
|
|
MemberDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_MEMBER_OBJ_UPDATE_FAILED;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
DPRINT0("No update required\n");
|
|
}
|
|
|
|
|
|
if (bMakeMePrimary && (Status == MKDSOE_SUCCESS)) {
|
|
//
|
|
// Update the primary member attribute on the replica set object to
|
|
// reference this member if /makemeprimary is set.
|
|
//
|
|
Status = UpdateReplicaSet(NTFRSSettingsDn, ReplicaSetName,
|
|
NULL, NULL, NULL, MemberName, NULL);
|
|
}
|
|
|
|
CLEANUP:
|
|
|
|
FreeMod(&Mod);
|
|
FREE(ReplicaSetDn);
|
|
FREE(MemberDn);
|
|
FREE(SearchFilter);
|
|
FREE(CurComputerRef);
|
|
FREE(CurServerRef);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
CreateNewReplicaMember(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName,
|
|
PWCHAR MemberName,
|
|
PWCHAR ComputerDn,
|
|
PWCHAR ServerRef,
|
|
PWCHAR RefDCName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Create a new replica member.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
PWCHAR MemberDn = NULL;
|
|
|
|
ReplicaSetDn = ExtendDn(NTFRSSettingsDn, ReplicaSetName);
|
|
|
|
MemberDn = ExtendDn(ReplicaSetDn, MemberName);
|
|
|
|
// ATTR_DN
|
|
DPRINT1("MemberDn:%ws\n", MemberDn);
|
|
AddMod(ATTR_CLASS, ATTR_MEMBER, &Mod);
|
|
|
|
// ATTR_COMPUTER_REF
|
|
if (ComputerDn != NULL) {
|
|
AddMod(ATTR_COMPUTER_REF, ComputerDn, &Mod);
|
|
DPRINT1(" FrsComputerReference:%ws\n", ComputerDn);
|
|
}
|
|
|
|
// ATTR_SERVER_REF
|
|
if (ServerRef != NULL) {
|
|
AddMod(ATTR_SERVER_REF, ServerRef, &Mod);
|
|
DPRINT1(" ServerReference:%ws\n", ServerRef);
|
|
}
|
|
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", MemberDn);
|
|
} else {
|
|
LStatus = ldap_add_s(pLdap, MemberDn, Mod);
|
|
|
|
if ((LStatus == LDAP_CONSTRAINT_VIOLATION) && RefDCName != NULL) {
|
|
//
|
|
// prepare the server hint. Needed in case the member object
|
|
// is just created on another DC than the one on which the
|
|
// subscriber is being created.
|
|
//
|
|
LDAPControl simpleControl;
|
|
PLDAPControl controlArray[2];
|
|
INT rc;
|
|
BERVAL* pBerVal = NULL;
|
|
BerElement* pBer;
|
|
|
|
pBer = ber_alloc_t(LBER_USE_DER);
|
|
if (!pBer) {
|
|
Status = MKDSOE_MEMBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
DPRINT1("Sending binding DC Name %ws\n",RefDCName);
|
|
rc = ber_printf(pBer,"{io}", 0, RefDCName, wcslen(RefDCName) * sizeof(WCHAR));
|
|
if ( rc == -1 ) {
|
|
Status = MKDSOE_MEMBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
rc = ber_flatten(pBer, &pBerVal);
|
|
if (rc == -1) {
|
|
Status = MKDSOE_MEMBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
ber_free(pBer,1);
|
|
|
|
controlArray[0] = &simpleControl;
|
|
controlArray[1] = NULL;
|
|
|
|
// simpleControl.ldctl_oid = LDAP_SERVER_GC_VERIFY_NAME_OID_W;
|
|
simpleControl.ldctl_oid = LDAP_SERVER_VERIFY_NAME_OID_W;
|
|
simpleControl.ldctl_iscritical = TRUE;
|
|
simpleControl.ldctl_value = *pBerVal;
|
|
|
|
LStatus = ldap_add_ext_s(pLdap,
|
|
MemberDn,
|
|
Mod,
|
|
(PLDAPControl *)&controlArray, //ServerControls,
|
|
NULL //ClientControls,
|
|
);
|
|
}
|
|
|
|
if (LStatus == LDAP_ALREADY_EXISTS) {
|
|
//
|
|
// If the object already exists then convert the create to an update.
|
|
// This is to allow the user to run the data file with creates twice without
|
|
// generating errors but only fixing the objects that have changed.
|
|
//
|
|
Status = UpdateReplicaMember(NTFRSSettingsDn,
|
|
ReplicaSetName,
|
|
MemberName,
|
|
ComputerDn,
|
|
ServerRef,
|
|
RefDCName);
|
|
|
|
} else if (LStatus != LDAP_SUCCESS) {
|
|
DPRINT2("ERROR - Can't create %ws: %ws\n", MemberDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_MEMBER_OBJ_CRE_FAILED;
|
|
|
|
} else if (bMakeMePrimary) {
|
|
//
|
|
// Update the primary member attribute on the replica set object to
|
|
// reference this member if /makemeprimary is set.
|
|
//
|
|
Status = UpdateReplicaSet(NTFRSSettingsDn, ReplicaSetName,
|
|
NULL, NULL, NULL, MemberName, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
CLEANUP:
|
|
|
|
FreeMod(&Mod);
|
|
FREE(ReplicaSetDn);
|
|
FREE(MemberDn);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
DeleteReplicaSet(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
delete replica set.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
PWCHAR Attrs[2];
|
|
PLDAPMessage LdapEntry = NULL;
|
|
WCHAR SearchFilter[MAX_PATH];
|
|
DWORD NoOfSets;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
|
|
|
|
MK_ATTRS_1(Attrs, ATTR_DN);
|
|
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_CN, L"=", ReplicaSetName, L")" , CLASS_NTFRS_REPLICA_SET, L")");
|
|
|
|
if (!LdapSearchInit(pLdap, NTFRSSettingsDn, LDAP_SCOPE_SUBTREE, SearchFilter,
|
|
Attrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SET_DELETE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfSets = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfSets == 0) {
|
|
DPRINT0("Error deleting; connection not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SET_NOT_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfSets > 1) {
|
|
DPRINT0("Error deleting; duplicate sets found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SET_DUPS_FOUND_DELETE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
ReplicaSetDn = FindValue(LdapEntry, ATTR_DN);
|
|
|
|
DPRINT1("Deleting Dn:%ws\n", ReplicaSetDn);
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_delete_s(pLdap, %ws);\n", ReplicaSetDn);
|
|
} else {
|
|
LStatus = ldap_delete_s(pLdap, ReplicaSetDn);
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
DPRINT2("ERROR - Can't delete %ws: %ws\n",
|
|
ReplicaSetDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SET_DELETE_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
CLEANUP:
|
|
|
|
FreeMod(&Mod);
|
|
FREE(ReplicaSetDn);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
UpdateReplicaSet(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName,
|
|
PWCHAR ReplicaSetType,
|
|
PWCHAR FileFilter,
|
|
PWCHAR DirectoryFilter,
|
|
PWCHAR PrimaryMember,
|
|
PBYTE pSchedule
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Update the replica set parameters.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
PWCHAR Attrs[7];
|
|
PLDAPMessage LdapEntry = NULL;
|
|
WCHAR SearchFilter[MAX_PATH];
|
|
PWCHAR CurSetType = NULL;
|
|
PWCHAR CurFileFilter = NULL;
|
|
PWCHAR CurDirectoryFilter = NULL;
|
|
PWCHAR CurPrimaryMember = NULL;
|
|
PWCHAR PrimaryMemberDn = NULL;
|
|
DWORD NoOfSets;
|
|
PSCHEDULE Schedule = NULL;
|
|
DWORD ScheduleLen;
|
|
BOOL bNeedsUpdate = FALSE;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
|
|
|
|
MK_ATTRS_6(Attrs, ATTR_DN, ATTR_SET_TYPE, ATTR_FILE_FILTER, ATTR_DIRECTORY_FILTER, ATTR_SCHEDULE, ATTR_PRIMARY_MEMBER);
|
|
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_CN, L"=", ReplicaSetName, L")" , CLASS_NTFRS_REPLICA_SET, L")");
|
|
|
|
if (!LdapSearchInit(pLdap, NTFRSSettingsDn, LDAP_SCOPE_SUBTREE, SearchFilter,
|
|
Attrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SET_OBJ_UPDATE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfSets = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfSets == 0) {
|
|
DPRINT0("Error updating; connection not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SET_NOT_FOUND_UPDATE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (NoOfSets > 1) {
|
|
DPRINT0("Error updating; duplicate connections found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SET_DUPS_FOUND_UPDATE;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
ReplicaSetDn = FindValue(LdapEntry, ATTR_DN);
|
|
CurSetType = FindValue(LdapEntry, ATTR_SET_TYPE);
|
|
CurFileFilter = FindValue(LdapEntry, ATTR_FILE_FILTER);
|
|
CurDirectoryFilter = FindValue(LdapEntry, ATTR_DIRECTORY_FILTER);
|
|
CurPrimaryMember = FindValue(LdapEntry, ATTR_PRIMARY_MEMBER);
|
|
FindBerValue(LdapEntry, ATTR_SCHEDULE, &ScheduleLen, (VOID **)&Schedule);
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
DPRINT1("ReplicaSetDn:%ws\n", ReplicaSetDn);
|
|
|
|
// Check ATTR_SET_TYPE
|
|
// if a type is supplied then make sure it is same as the current type.
|
|
// if a type is not supplied then leave it as it is.
|
|
if (ReplicaSetType != NULL) {
|
|
if ((CurSetType == NULL) ||
|
|
((CurSetType != NULL) && wcscmp(ReplicaSetType, CurSetType))) {
|
|
AddMod(ATTR_SET_TYPE, ReplicaSetType, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsReplicaSetType:%ws\n", ReplicaSetType);
|
|
}
|
|
}
|
|
|
|
// Check ATTR_FILE_FILTER
|
|
// if a filter is supplied then make sure it is same as the current filter.
|
|
// if a filter is not supplied then leave it as it is.
|
|
if (FileFilter != NULL) {
|
|
if ((CurFileFilter == NULL) ||
|
|
((CurFileFilter != NULL) && wcscmp(FileFilter, CurFileFilter))) {
|
|
AddMod(ATTR_FILE_FILTER, FileFilter, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsFileFilter:%ws\n", FileFilter);
|
|
}
|
|
}
|
|
|
|
// Check ATTR_DIRECTORY_FILTER
|
|
// if a filter is supplied then make sure it is same as the current filter.
|
|
// if a filter is not supplied then leave it as it is.
|
|
if (DirectoryFilter != NULL) {
|
|
if ((CurDirectoryFilter == NULL) ||
|
|
((CurDirectoryFilter != NULL) && wcscmp(DirectoryFilter, CurDirectoryFilter))) {
|
|
AddMod(ATTR_DIRECTORY_FILTER, DirectoryFilter, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsDirectoryFilter:%ws\n", DirectoryFilter);
|
|
}
|
|
}
|
|
|
|
// Check ATTR_PRIMARY_MEMBER
|
|
// if a PrimaryMember is supplied then make sure it is same as the current PrimaryMember.
|
|
// if a PrimaryMember is not supplied then leave it as it is.
|
|
if (PrimaryMember != NULL) {
|
|
PrimaryMemberDn = ExtendDn(ReplicaSetDn, PrimaryMember);
|
|
if ((CurPrimaryMember == NULL) ||
|
|
((CurPrimaryMember != NULL) && wcscmp(PrimaryMemberDn, CurPrimaryMember))) {
|
|
AddMod(ATTR_PRIMARY_MEMBER, PrimaryMemberDn, &Mod);
|
|
bNeedsUpdate = TRUE;
|
|
DPRINT1(" New FrsPrimaryMember:%ws\n", PrimaryMemberDn);
|
|
}
|
|
}
|
|
|
|
// Check ATTR_SCHEDULE
|
|
if (pSchedule != NULL) {
|
|
if ((Schedule == NULL) ||
|
|
(FRST_SIZE_OF_SCHEDULE != ScheduleLen) ||
|
|
(memcmp(Schedule, pSchedule, FRST_SIZE_OF_SCHEDULE))) {
|
|
bNeedsUpdate = TRUE;
|
|
AddBerMod(ATTR_SCHEDULE,(PCHAR)pSchedule,FRST_SIZE_OF_SCHEDULE,&Mod);
|
|
|
|
DPRINT0(" New schedule:\n");
|
|
PrintSchedule((PSCHEDULE)pSchedule, 0x0F);
|
|
}
|
|
}
|
|
|
|
if (bNeedsUpdate) {
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_modify_s(pLdap, %ws, Mod);\n", ReplicaSetDn);
|
|
} else {
|
|
LStatus = ldap_modify_s(pLdap, ReplicaSetDn, Mod);
|
|
if (LStatus != LDAP_SUCCESS) {
|
|
DPRINT2("ERROR - Can't update %ws: %ws\n",
|
|
ReplicaSetDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SET_OBJ_UPDATE_FAILED;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
DPRINT0("No update required\n");
|
|
}
|
|
|
|
CLEANUP:
|
|
|
|
FreeMod(&Mod);
|
|
FREE(ReplicaSetDn);
|
|
FREE(PrimaryMemberDn);
|
|
FREE(CurSetType);
|
|
FREE(CurFileFilter);
|
|
FREE(CurDirectoryFilter);
|
|
FREE(CurPrimaryMember);
|
|
FREE(Schedule);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
CreateNewReplicaSet(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName,
|
|
PWCHAR ReplicaSetType,
|
|
PWCHAR FileFilter,
|
|
PWCHAR DirectoryFilter,
|
|
PWCHAR PrimaryMember,
|
|
PBYTE pSchedule
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Create a new replica Set.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
LDAPMod **Mod = NULL;
|
|
DWORD LStatus = LDAP_SUCCESS;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
PWCHAR PrimaryMemberDn = NULL;
|
|
UINT i;
|
|
|
|
ReplicaSetDn = ExtendDn(NTFRSSettingsDn, ReplicaSetName);
|
|
|
|
// ATTR_DN
|
|
DPRINT1("ReplicaSetDn:%ws\n", ReplicaSetDn);
|
|
AddMod(ATTR_CLASS, ATTR_REPLICA_SET, &Mod);
|
|
|
|
// ATTR_SET_TYPE
|
|
AddMod(ATTR_SET_TYPE, ReplicaSetType, &Mod);
|
|
DPRINT1(" FrsReplicaSetType:%ws\n", ReplicaSetType);
|
|
|
|
// ATTR_FILE_FILTER
|
|
if (FileFilter != NULL) {
|
|
AddMod(ATTR_FILE_FILTER, FileFilter, &Mod);
|
|
DPRINT1(" FrsFileFilter:%ws\n", FileFilter);
|
|
}
|
|
|
|
// ATTR_DIRECTORY_FILTER
|
|
if (DirectoryFilter != NULL) {
|
|
AddMod(ATTR_DIRECTORY_FILTER, DirectoryFilter, &Mod);
|
|
DPRINT1(" FrsDirectoryFilter:%ws\n", DirectoryFilter);
|
|
}
|
|
|
|
// ATTR_PRIMARY_MEMBER
|
|
if (PrimaryMember != NULL) {
|
|
PrimaryMemberDn = ExtendDn(ReplicaSetDn, PrimaryMember);
|
|
AddMod(ATTR_PRIMARY_MEMBER, PrimaryMemberDn, &Mod);
|
|
DPRINT1(" FrsPrimaryMemberr:%ws\n", PrimaryMemberDn);
|
|
}
|
|
|
|
if (pSchedule != NULL) {
|
|
AddBerMod(ATTR_SCHEDULE,(PCHAR)pSchedule,FRST_SIZE_OF_SCHEDULE,&Mod);
|
|
|
|
PrintSchedule((PSCHEDULE)pSchedule, 0x0F);
|
|
}
|
|
|
|
if (bDebugMode) {
|
|
DPRINT1("LStatus = ldap_add_s(pLdap, %ws, Mod);\n", ReplicaSetDn);
|
|
} else {
|
|
LStatus = ldap_add_s(pLdap, ReplicaSetDn, Mod);
|
|
|
|
if (LStatus == LDAP_ALREADY_EXISTS) {
|
|
//
|
|
// If the object already exists then convert the create to an update.
|
|
// This is to allow the user to run the data file with creates twice without
|
|
// generating errors but only fixing the objects that have changed.
|
|
//
|
|
Status = UpdateReplicaSet(NTFRSSettingsDn,
|
|
ReplicaSetName,
|
|
ReplicaSetType,
|
|
FileFilter,
|
|
DirectoryFilter,
|
|
PrimaryMember,
|
|
pSchedule);
|
|
|
|
} else if (LStatus != LDAP_SUCCESS) {
|
|
DPRINT2("ERROR - Can't create %ws: %ws\n",
|
|
ReplicaSetDn, ldap_err2string(LStatus));
|
|
Status = MKDSOE_SET_OBJ_CRE_FAILED;
|
|
}
|
|
}
|
|
|
|
FreeMod(&Mod);
|
|
FREE(ReplicaSetDn);
|
|
FREE(PrimaryMemberDn);
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
PrintScheduleGrid(
|
|
PUCHAR ScheduleData,
|
|
DWORD Mask
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Print the schedule grid.
|
|
|
|
Arguments:
|
|
Schedule
|
|
Mask for each byte.
|
|
Return Value:
|
|
NONE
|
|
--*/
|
|
{
|
|
DWORD Day, Hour;
|
|
|
|
for (Day = 0; Day < 7; ++Day) {
|
|
printf(" Day %1d: ",Day + 1);
|
|
for (Hour = 0; Hour < 24; ++Hour) {
|
|
printf("%1x", *(ScheduleData + (Day * 24) + Hour) & Mask);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
PrintSchedule(
|
|
PSCHEDULE Schedule,
|
|
DWORD Mask
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Print the schedule.
|
|
|
|
Arguments:
|
|
Schedule
|
|
Mask for each byte.
|
|
Return Value:
|
|
NONE
|
|
--*/
|
|
{
|
|
PUCHAR ScheduleData;
|
|
DWORD i;
|
|
|
|
if (bVerboseMode) {
|
|
printf(" schedule:\n");
|
|
for (i = 0; i < Schedule->NumberOfSchedules; ++i) {
|
|
ScheduleData = ((PUCHAR)Schedule) + Schedule->Schedules[i].Offset;
|
|
if (Schedule->Schedules[i].Type != SCHEDULE_INTERVAL) {
|
|
continue;
|
|
}
|
|
PrintScheduleGrid(ScheduleData, Mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
DumpSubscribers(
|
|
PWCHAR ComputerDn,
|
|
PWCHAR MemberDn
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Dump the frs member object.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
DWORD LStatus;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR Attrs[5];
|
|
PLDAPMessage LdapEntry = NULL;
|
|
PWCHAR Val = NULL;
|
|
PWCHAR SearchFilter = NULL;
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
DWORD NoOfSubscribers;
|
|
|
|
MK_ATTRS_4(Attrs, ATTR_DN, ATTR_MEMBER_REF, ATTR_REPLICA_ROOT, ATTR_REPLICA_STAGE);
|
|
|
|
SearchFilter = (PWCHAR) malloc((((MemberDn != NULL)?wcslen(MemberDn):0)
|
|
+ MAX_PATH)
|
|
* sizeof(WCHAR));
|
|
|
|
if (MemberDn != NULL) {
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_MEMBER_REF, L"=", MemberDn, L")" , CLASS_SUBSCRIBER, L")");
|
|
} else {
|
|
wcscpy(SearchFilter, CLASS_SUBSCRIBER);
|
|
}
|
|
|
|
if (!LdapSearchInit(pLdap, ComputerDn, LDAP_SCOPE_SUBTREE, SearchFilter,
|
|
Attrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SUBSCRIBER_DUMP_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfSubscribers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfSubscribers == 0) {
|
|
LdapSearchClose(&FrsSearchContext);
|
|
if (MemberDn != NULL) {
|
|
//
|
|
// This error return only makes sense when we were asked to dump
|
|
// a specific subscriber.
|
|
//
|
|
DPRINT0("Error dumping; subscriber not found.\n");
|
|
Status = MKDSOE_SUBSCRIBER_NOT_FOUND_DUMP;
|
|
}
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
// ATTR_DN
|
|
Val = FindValue(LdapEntry, ATTR_DN);
|
|
printf("\n SubscriberDn:%ws\n", Val);
|
|
FREE(Val);
|
|
|
|
//
|
|
// ATTR_CLASS
|
|
// We know the class
|
|
printf(" ObjectClass:nTFRSSubscriber\n");
|
|
|
|
// ATTR_MEMBER_REF
|
|
Val = FindValue(LdapEntry, ATTR_MEMBER_REF);
|
|
printf(" FrsMemberReference:%ws\n", Val);
|
|
FREE(Val);
|
|
|
|
// ATTR_REPLICA_ROOT
|
|
Val = FindValue(LdapEntry, ATTR_REPLICA_ROOT);
|
|
printf(" FrsRootPath:%ws\n", Val);
|
|
FREE(Val);
|
|
|
|
// ATTR_REPLICA_STAGE
|
|
Val = FindValue(LdapEntry, ATTR_REPLICA_STAGE);
|
|
printf(" FrsStagingPath:%ws\n", Val);
|
|
FREE(Val);
|
|
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
CLEANUP:
|
|
|
|
FREE(SearchFilter);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
DumpReplicaMembers(
|
|
PWCHAR NTFRSReplicaSetDn,
|
|
PWCHAR MemberName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Dump the frs member object.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
DWORD LStatus;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR Attrs[4];
|
|
PLDAPMessage LdapEntry = NULL;
|
|
PWCHAR Val = NULL;
|
|
PWCHAR ComputerRef = NULL;
|
|
PWCHAR MemberDn = NULL;
|
|
WCHAR SearchFilter[MAX_PATH];
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
DWORD NoOfMembers;
|
|
|
|
MK_ATTRS_3(Attrs, ATTR_DN, ATTR_COMPUTER_REF, ATTR_SERVER_REF);
|
|
|
|
if (MemberName != NULL) {
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_CN, L"=", MemberName, L")" , CLASS_MEMBER, L")");
|
|
} else {
|
|
wcscpy(SearchFilter, CLASS_MEMBER);
|
|
}
|
|
|
|
if (!LdapSearchInit(pLdap, NTFRSReplicaSetDn, LDAP_SCOPE_ONELEVEL, SearchFilter,
|
|
Attrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_MEMBER_DUMP_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfMembers = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfMembers == 0) {
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
if (MemberName != NULL) {
|
|
//
|
|
// This error return only makes sense when we were asked to dump
|
|
// a specific member object.
|
|
//
|
|
DPRINT0("Error dumping; member not found.\n");
|
|
Status = MKDSOE_MEMBER_NOT_FOUND_DUMP;
|
|
}
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
// ATTR_DN
|
|
MemberDn = FindValue(LdapEntry, ATTR_DN);
|
|
printf("\n MemberDn:%ws\n", MemberDn);
|
|
|
|
//
|
|
// ATTR_CLASS
|
|
// We know the class
|
|
printf(" ObjectClass:nTFRSMember\n");
|
|
|
|
// ATTR_COMPUTER_REF
|
|
ComputerRef = FindValue(LdapEntry, ATTR_COMPUTER_REF);
|
|
printf(" FrsComputerReference:%ws\n", ComputerRef);
|
|
|
|
// ATTR_SERVER_REF
|
|
Val = FindValue(LdapEntry, ATTR_SERVER_REF);
|
|
printf(" ServerReference:%ws\n", Val);
|
|
FREE(Val);
|
|
|
|
if (bAffectAll && ComputerRef != NULL) {
|
|
DumpSubscribers(ComputerRef, MemberDn);
|
|
}
|
|
|
|
FREE(ComputerRef);
|
|
FREE(MemberDn);
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
CLEANUP:
|
|
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
DumpReplicaSet(
|
|
PWCHAR NTFRSSettingsDn,
|
|
PWCHAR ReplicaSetName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Dump the replica set object.
|
|
Arguments:
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
DWORD LStatus;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
PWCHAR Attrs[7];
|
|
PLDAPMessage LdapEntry = NULL;
|
|
PWCHAR Val = NULL;
|
|
PWCHAR ReplicaSetDn = NULL;
|
|
WCHAR SearchFilter[MAX_PATH];
|
|
FRS_LDAP_SEARCH_CONTEXT FrsSearchContext;
|
|
DWORD ReplicaSetType;
|
|
DWORD NoOfSets;
|
|
DWORD ScheduleLen;
|
|
PSCHEDULE Schedule = NULL;
|
|
BOOL SaveVerbose;
|
|
|
|
|
|
MK_ATTRS_6(Attrs, ATTR_DN, ATTR_SET_TYPE, ATTR_FILE_FILTER, ATTR_DIRECTORY_FILTER, ATTR_SCHEDULE, ATTR_PRIMARY_MEMBER);
|
|
|
|
if (ReplicaSetName != NULL) {
|
|
WCS_CAT7(SearchFilter, L"(&(", ATTR_CN, L"=", ReplicaSetName, L")" , CLASS_NTFRS_REPLICA_SET, L")");
|
|
} else {
|
|
wcscpy(SearchFilter, CLASS_NTFRS_REPLICA_SET);
|
|
}
|
|
|
|
if (!LdapSearchInit(pLdap, NTFRSSettingsDn, LDAP_SCOPE_SUBTREE, SearchFilter,
|
|
Attrs, 0, &FrsSearchContext, FALSE)) {
|
|
Status = MKDSOE_SET_DUMP_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
NoOfSets = FrsSearchContext.EntriesInPage;
|
|
|
|
if (NoOfSets == 0) {
|
|
DPRINT0("Error dumping; replica set not found.\n");
|
|
LdapSearchClose(&FrsSearchContext);
|
|
Status = MKDSOE_SET_NOT_FOUND_DUMP;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Scan the entries returned from ldap_search
|
|
//
|
|
for (LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext);
|
|
LdapEntry != NULL;
|
|
LdapEntry = LdapSearchNext(pLdap, &FrsSearchContext)) {
|
|
|
|
// ATTR_DN
|
|
ReplicaSetDn = FindValue(LdapEntry, ATTR_DN);
|
|
printf("\nReplicaSetDn:%ws\n", ReplicaSetDn);
|
|
|
|
//
|
|
// ATTR_CLASS
|
|
// We know the class
|
|
printf(" ObjectClass:nTFRSReplicaSet\n");
|
|
|
|
// ATTR_SET_TYPE
|
|
Val = FindValue(LdapEntry, ATTR_SET_TYPE);
|
|
ReplicaSetType = _wtoi(Val);
|
|
printf(" FrsReplicaSetType:%ws ", Val);
|
|
|
|
if (ReplicaSetType > MKDSOE_RSTYPE_MAX) {
|
|
printf("[ Invalid Type ");
|
|
} else {
|
|
printf("[ %ws ", ReplicaSetTypeStr[ReplicaSetType]);
|
|
}
|
|
printf("]\n");
|
|
FREE(Val);
|
|
|
|
// ATTR_FILE_FILTER
|
|
Val = FindValue(LdapEntry, ATTR_FILE_FILTER);
|
|
printf(" FrsFileFilter:%ws\n", Val);
|
|
FREE(Val);
|
|
|
|
// ATTR_DIRECTORY_FILTER
|
|
Val = FindValue(LdapEntry, ATTR_DIRECTORY_FILTER);
|
|
printf(" FrsDirectoryFilter:%ws\n", Val);
|
|
FREE(Val);
|
|
|
|
// ATTR_PRIMARY_MEMBER
|
|
Val = FindValue(LdapEntry, ATTR_PRIMARY_MEMBER);
|
|
printf(" FrsPrimaryMember:%ws\n", Val);
|
|
FREE(Val);
|
|
|
|
// ATTR_SCHEDULE
|
|
FindBerValue(LdapEntry, ATTR_SCHEDULE, &ScheduleLen, (VOID **)&Schedule);
|
|
|
|
if (Schedule) {
|
|
SaveVerbose = bVerboseMode;
|
|
bVerboseMode = TRUE;
|
|
PrintSchedule(Schedule, 0x0F);
|
|
bVerboseMode = SaveVerbose;
|
|
delete(Schedule);
|
|
}
|
|
|
|
//
|
|
// Dump the members if asked.
|
|
//
|
|
if (bAffectAll) {
|
|
DumpReplicaMembers(ReplicaSetDn, NULL);
|
|
}
|
|
FREE(ReplicaSetDn);
|
|
}
|
|
|
|
LdapSearchClose(&FrsSearchContext);
|
|
|
|
CLEANUP:
|
|
|
|
return Status;
|
|
}
|
|
|
|
PWCHAR *
|
|
ConvertArgv(
|
|
DWORD argc,
|
|
PCHAR *argv
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Convert short char argv into wide char argv
|
|
|
|
Arguments:
|
|
argc - From main
|
|
argv - From main
|
|
|
|
Return Value:
|
|
Address of the new argv
|
|
--*/
|
|
{
|
|
PWCHAR *wideargv;
|
|
|
|
wideargv = new PWCHAR[argc + 1];
|
|
wideargv[argc] = NULL;
|
|
|
|
while (argc-- >= 1) {
|
|
wideargv[argc] = new WCHAR[strlen(argv[argc]) + 1];
|
|
wsprintf(wideargv[argc], L"%hs", argv[argc]);
|
|
}
|
|
return wideargv;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
FreeArgv(
|
|
DWORD Argc,
|
|
PWCHAR *Argv
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Free the converted arguments.
|
|
|
|
Arguments:
|
|
Argc - No of arguments.
|
|
Argv - Converted arguments returned from ConvertArgv.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
|
|
while (Argc-- >= 1) {
|
|
FREE(Argv[Argc]);
|
|
}
|
|
FREE(Argv);
|
|
}
|
|
|
|
VOID
|
|
Usage(
|
|
PWCHAR *Argv
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Tell the user how to use the program.
|
|
|
|
Arguments:
|
|
Argv Argument array.
|
|
|
|
Return Value:
|
|
None
|
|
--*/
|
|
{
|
|
printf("\n");
|
|
printf("%-60s\n", "This tool creates, adds, updates, dumps, and deletes replica set, member, and subscriber objects.\n");
|
|
printf("%-60s%ws /?\n", "Help", Argv[0]);
|
|
printf("%-60s%ws /v\n", "Verbose mode.", Argv[0]);
|
|
printf("%-60s%ws /debug\n", "Debug mode. No Writes to the DC.", Argv[0]);
|
|
printf("%-60s%ws /dc\n", "Name of the DC to connect to.", Argv[0]);
|
|
printf("%-60s%ws /ntfrssettingsdn\n", "Dn for the FRS settings container.", Argv[0]);
|
|
printf("%-60s%ws /setname\n", "Name of the replica set.", Argv[0]);
|
|
printf("%-60s%ws /settype\n", "Type of the replica set.", Argv[0]);
|
|
printf("%-60s\n", "Sysvol = 2");
|
|
printf("%-60s\n", "Dfs = 3");
|
|
printf("%-60s\n\n", "Other = 4");
|
|
printf("%-60s%ws /filefilter\n", "Filter to use against files.e.g. *.bak,*.tmp", Argv[0]);
|
|
printf("%-60s%ws /directoryfilter\n", "Filter to use against directories.", Argv[0]);
|
|
printf("%-60s%ws /primarymember\n", "Name of primary member for initial replica set contents.", Argv[0]);
|
|
printf("%-60s%ws /membername\n", "Name of the member.", Argv[0]);
|
|
printf("%-60s\n", "If DFS naming conventions are desired, specify 'ComputerObjectGuid' for membername.");
|
|
printf("%-60s%ws /dfsname\n", "Name of the dfs in the format <root name>|<junction name>.", Argv[0]);
|
|
printf("%-60s\n", "Required if membername is 'ComputerObjectGuid'.");
|
|
printf("%-60s%ws /computerdn\n", "Dn for the computer object.", Argv[0]);
|
|
printf("%-60s%ws /computername\n", "NT4 style computer name. e.g. NTDEV\\SUDARCTEST$.", Argv[0]);
|
|
printf("%-60s%ws /serverref\n", "Dn of NTDSSettings object for DCs.", Argv[0]);
|
|
printf("%-60s%ws /rootpath\n", "Replica root path. Has to be absolute.", Argv[0]);
|
|
printf("%-60s%ws /stagepath\n", "Replica staging path. Has to be absolute.", Argv[0]);
|
|
printf("%-60s%ws /workingpath\n", "Replica working path. Has to be absolute. Only used if subscriptions obj created.", Argv[0]);
|
|
printf("%-60s%ws /refdcname <dnsname>\n", "Reference DC to use while creating subscriber.", Argv[0]);
|
|
printf("%-60s%ws /[create<object> update<object> del<object> dump]\n", "Operation to be performed.", Argv[0]);
|
|
printf("%-60s%ws \n\n", "<object> can be one of [set member subscriber].", Argv[0]);
|
|
printf("%-60s%ws /all\n", "Perform the operation on all the objects.", Argv[0]);
|
|
printf("%-60s%ws \n\n", "/all only works with /dump and /del.", Argv[0]);
|
|
printf("%-60s%ws /makemeprimary\n", "Make this member object the primary replica set member.", Argv[0]);
|
|
printf("%-60s%ws \n\n", "Applies to member operations only.", Argv[0]);
|
|
printf("%-60s%ws /schedule <Interval> <Stagger> <Offset>\n", "Schedule to create for the replica set.", Argv[0]);
|
|
printf("%-60s%ws <Interval>\n", "The desired interval between each sync with one source.", Argv[0]);
|
|
printf("%-60s%ws <Stagger>\n", "Typically number of source DCs.", Argv[0]);
|
|
printf("%-60s%ws <Offset>\n\n", "Typically the number of the source DC.", Argv[0]);
|
|
printf("%-60s%ws /schedoverride\n", "File with 7x24 vector of schedule override data.", Argv[0]);
|
|
printf("%-60s%ws /schedmask\n", "File with 7x24 vector of schedule mask off data.", Argv[0]);
|
|
printf("%-60s\n", "SchedOverride and SchedMask data are formatted");
|
|
printf("%-60s\n\n", "as 2 ascii hex digits for each schedule byte.");
|
|
DPRINT0("\n");
|
|
|
|
DPRINT0("mkdso.exe error return codes\n");
|
|
DPRINT0("100 = Success\n");
|
|
DPRINT0("101 = Invalid Arguments\n");
|
|
DPRINT0("102 = Could not bind to the DC\n");
|
|
DPRINT0("103 = Could not find 'NTFRS Settings' object. Check the /settingsdn parameter\n");
|
|
DPRINT0("104 = Error creating replica set\n");
|
|
DPRINT0("105 = Error updating replica set\n");
|
|
DPRINT0("106 = Error updating replica set; set not found\n");
|
|
DPRINT0("107 = Error updating replica set; duplicate sets found\n");
|
|
DPRINT0("108 = Error deleting replica set; duplicate sets found.\n");
|
|
DPRINT0("109 = Error deleting replica set\n");
|
|
DPRINT0("110 = Error deleting replica set; set not found\n");
|
|
DPRINT0("111 = Deleting multiple sets\n");
|
|
DPRINT0("112 = Error dumping replica set\n");
|
|
DPRINT0("113 = Error dumping replica set; set not found\n");
|
|
DPRINT0("114 = Dumping duplicate sets\n");
|
|
DPRINT0("115 = Error creating replica member\n");
|
|
DPRINT0("116 = Error updating replica member\n");
|
|
DPRINT0("117 = Error updating replica member; member not found\n");
|
|
DPRINT0("118 = Error updating replica member; duplicate members found\n");
|
|
DPRINT0("119 = Error deleting member; duplicate subscribers found.\n");
|
|
DPRINT0("120 = Error deleting replica member\n");
|
|
DPRINT0("121 = Error deleting replica member; member not found\n");
|
|
DPRINT0("122 = Deleting multiple members\n");
|
|
DPRINT0("123 = Error dumping replica member\n");
|
|
DPRINT0("124 = Error dumping replica member; member not found\n");
|
|
DPRINT0("125 = Dumping duplicate members\n");
|
|
DPRINT0("126 = Error creating subscriber\n");
|
|
DPRINT0("127 = Error updating subscriber\n");
|
|
DPRINT0("128 = Error updating subscriber; subscriber not found\n");
|
|
DPRINT0("129 = Error updating subscriber; duplicate subscribers found\n");
|
|
DPRINT0("130 = Error deleting subscriber\n");
|
|
DPRINT0("131 = Error deleting subscriber; subscriber not found\n");
|
|
DPRINT0("132 = Deleting multiple subscribers\n");
|
|
DPRINT0("133 = Error deleting subscriber; duplicate subscribers found.\n");
|
|
DPRINT0("134 = Error dumping subscriber\n");
|
|
DPRINT0("135 = Error dumping subscriber; subscriber not found\n");
|
|
DPRINT0("136 = Dumping duplicate subscribers\n");
|
|
DPRINT0("\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
DWORD __cdecl
|
|
main(
|
|
DWORD argc,
|
|
PCHAR *argv
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
Exits with 0 if everything went okay. Otherwise returns a error code.
|
|
|
|
MKDSOE_SUCCESS "Success."
|
|
MKDSOE_BAD_ARG "Invalid Arguments."
|
|
MKDSOE_CANT_BIND "Could not bind to the DC."
|
|
MKDSOE_NO_NTFRS_SETTINGS "Could not find 'NTFRS Settings' object. Check the /settingsdn parameter."
|
|
MKDSOE_SET_OBJ_CRE_FAILED "Error creating replica set."
|
|
MKDSOE_SET_OBJ_UPDATE_FAILED "Error updating replica set."
|
|
MKDSOE_SET_NOT_FOUND_UPDATE "Error updating replica set; set not found."
|
|
MKDSOE_SET_DUPS_FOUND_UPDATE "Error updating replica set; duplicate sets found."
|
|
MKDSOE_SET_DUPS_FOUND_DELETE "Error deleting replica set; duplicate sets found."
|
|
MKDSOE_SET_DELETE_FAILED "Error deleting replica set."
|
|
MKDSOE_SET_NOT_FOUND_DELETE "Error deleting replica set; set not found."
|
|
MKDSOE_MULTIPLE_SETS_DELETED "Deleting multiple sets."
|
|
MKDSOE_SET_DUMP_FAILED "Error dumping replica set."
|
|
MKDSOE_SET_NOT_FOUND_DUMP "Error dumping replica set; set not found."
|
|
MKDSOE_MULTIPLE_SETS_DUMPED "Dumping duplicate sets."
|
|
MKDSOE_MEMBER_OBJ_CRE_FAILED "Error creating replica member."
|
|
MKDSOE_MEMBER_OBJ_UPDATE_FAILED "Error updating replica member."
|
|
MKDSOE_MEMBER_NOT_FOUND_UPDATE "Error updating replica member; member not found."
|
|
MKDSOE_MEMBER_DUPS_FOUND_UPDATE "Error updating replica member; duplicate members found."
|
|
MKDSOE_MEMBER_DUPS_FOUND_DELETE "Error deleting member; duplicate subscribers found."
|
|
MKDSOE_MEMBER_DELETE_FAILED "Error deleting replica member."
|
|
MKDSOE_MEMBER_NOT_FOUND_DELETE "Error deleting replica member; member not found."
|
|
MKDSOE_MULTIPLE_MEMBERS_DELETED "Deleting multiple members."
|
|
MKDSOE_MEMBER_DUMP_FAILED "Error dumping replica member."
|
|
MKDSOE_MEMBER_NOT_FOUND_DUMP "Error dumping replica member; member not found."
|
|
MKDSOE_MULTIPLE_MEMBERS_DUMPED "Dumping duplicate members."
|
|
MKDSOE_SUBSCRIBER_OBJ_CRE_FAILED "Error creating subscriber."
|
|
MKDSOE_SUBSCRIBER_OBJ_UPDATE_FAILED "Error updating subscriber."
|
|
MKDSOE_SUBSCRIBER_NOT_FOUND_UPDATE "Error updating subscriber; subscriber not found."
|
|
MKDSOE_SUBSCRIBER_DUPS_FOUND_UPDATE "Error updating subscriber; duplicate subscribers found."
|
|
MKDSOE_SUBSCRIBER_DELETE_FAILED "Error deleting subscriber."
|
|
MKDSOE_SUBSCRIBER_NOT_FOUND_DELETE "Error deleting subscriber; subscriber not found."
|
|
MKDSOE_MULTIPLE_SUBSCRIBERS_DELETED "Deleting multiple subscribers."
|
|
MKDSOE_SUBSCRIBER_DUPS_FOUND_DELETE "Error deleting subscriber; duplicate subscribers found."
|
|
MKDSOE_SUBSCRIBER_DUMP_FAILED "Error dumping subscriber."
|
|
MKDSOE_SUBSCRIBER_NOT_FOUND_DUMP "Error dumping subscriber; subscriber not found."
|
|
MKDSOE_MULTIPLE_SUBSCRIBERS_DUMPED "Dumping duplicate subscribers."
|
|
--*/
|
|
{
|
|
PWCHAR *Argv;
|
|
ULONG i, j;
|
|
ULONG OptLen;
|
|
DWORD Status = MKDSOE_SUCCESS;
|
|
DWORD WStatus = ERROR_SUCCESS;
|
|
PWCHAR NTFRSSettingsDn = NULL;
|
|
PWCHAR ReplicaSetName = NULL;
|
|
PWCHAR ReplicaSetType = NULL;
|
|
PWCHAR FileFilter = NULL;
|
|
PWCHAR DirectoryFilter = NULL;
|
|
PWCHAR PrimaryMember = NULL;
|
|
PBYTE pSchedule = NULL;
|
|
DWORD Interval = 1;
|
|
DWORD Stagger = 1;
|
|
DWORD Offset = 0;
|
|
DWORD Vbar;
|
|
PWCHAR NT4ComputerName = NULL;
|
|
PWCHAR ComputerDn = NULL;
|
|
PWCHAR DomainName = NULL;
|
|
PWCHAR MemberName = NULL;
|
|
PWCHAR ServerRef = NULL;
|
|
PWCHAR RootPath = NULL;
|
|
PWCHAR StagePath = NULL;
|
|
PWCHAR WorkingPath = NULL;
|
|
PWCHAR RefDCName = NULL;
|
|
PWCHAR *Values = NULL;
|
|
PWCHAR Switch = NULL;
|
|
PWCHAR pw;
|
|
BOOL ClassFound = FALSE;
|
|
|
|
DWORD Commands = 0;
|
|
DS_NAME_RESULT *Cracked = NULL;
|
|
HANDLE hDs = INVALID_HANDLE_VALUE;
|
|
|
|
PLDAP_BERVAL *BerValues = NULL;
|
|
GUID *Guid = NULL;
|
|
|
|
PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
|
|
BOOL bRetry = FALSE;
|
|
DWORD DcFlags;
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pBuffer = NULL;
|
|
|
|
|
|
Argv = ConvertArgv(argc, argv);
|
|
|
|
if (argc <= 1) {
|
|
Usage(Argv);
|
|
Status = MKDSOE_SUCCESS;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
|
|
for (i = 1; i < argc; ++i) {
|
|
OptLen = wcslen(Argv[i]);
|
|
_wcslwr(Argv[i]);
|
|
Switch = Argv[i];
|
|
DPRINT1(" %20ws ", Switch);
|
|
|
|
if ((*Switch != L'/') && (*Switch != L'-')) {
|
|
Usage(Argv);
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
++Switch; // jump over -
|
|
|
|
if (OptLen == 2 && ArgMatch(Switch, L"v")) {
|
|
bVerboseMode=TRUE;
|
|
DPRINT1(" %20ws ", Switch);
|
|
|
|
} else if (OptLen == 3 && ArgMatch(Switch, L"vs")) {
|
|
bVerboseModeSearch=TRUE;
|
|
DPRINT1(" %20ws ", Switch);
|
|
|
|
} else if (OptLen == 2 && ArgMatch(Switch, L"?")) {
|
|
Usage(Argv);
|
|
Status = MKDSOE_SUCCESS;
|
|
goto ARG_CLEANUP;
|
|
|
|
} else if (OptLen == 6 && ArgMatch(Switch, L"debug")) {
|
|
bDebugMode = TRUE;
|
|
bVerboseMode=TRUE;
|
|
|
|
} else if (OptLen == 3 && (i+1 < argc) && ArgMatch(Switch, L"dc")) {
|
|
i += 1;
|
|
DcName = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(DcName, Argv[i]);
|
|
DPRINT1("%ws", DcName);
|
|
|
|
} else if (OptLen == 16 && (i+1 < argc) && ArgMatch(Switch, L"ntfrssettingsdn")) {
|
|
i += 1;
|
|
NTFRSSettingsDn = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(NTFRSSettingsDn, Argv[i]);
|
|
DPRINT1("%ws", NTFRSSettingsDn);
|
|
|
|
} else if (OptLen == 8 && (i+1 < argc) && ArgMatch(Switch, L"setname")) {
|
|
i += 1;
|
|
ReplicaSetName = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(ReplicaSetName, Argv[i]);
|
|
DPRINT1("%ws", ReplicaSetName);
|
|
|
|
} else if (OptLen == 11 && (i+1 < argc) && ArgMatch(Switch, L"computerdn")) {
|
|
i += 1;
|
|
ComputerDn = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(ComputerDn, Argv[i]);
|
|
DPRINT1("%ws", ComputerDn);
|
|
|
|
} else if (OptLen == 13 && (i+1 < argc) && ArgMatch(Switch, L"computername")) {
|
|
i += 1;
|
|
NT4ComputerName = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(NT4ComputerName, Argv[i]);
|
|
DPRINT1("%ws", NT4ComputerName);
|
|
|
|
} else if (OptLen == 11 && (i+1 < argc) && ArgMatch(Switch, L"membername")) {
|
|
i += 1;
|
|
MemberName = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(MemberName, Argv[i]);
|
|
DPRINT1("%ws", MemberName);
|
|
|
|
} else if (OptLen == 11 && (i+1 < argc) && ArgMatch(Switch, L"membername")) {
|
|
i += 1;
|
|
MemberName = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(MemberName, Argv[i]);
|
|
DPRINT1("%ws", MemberName);
|
|
|
|
} else if (OptLen == 8 && (i+1 < argc) && ArgMatch(Switch, L"dfsname")) {
|
|
i += 1;
|
|
DFSName = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(DFSName, Argv[i]);
|
|
DPRINT1("%ws", DFSName);
|
|
bDFSNaming = TRUE;
|
|
|
|
} else if (OptLen == 9 && (i+1 < argc) && ArgMatch(Switch, L"rootpath")) {
|
|
i += 1;
|
|
RootPath = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(RootPath, Argv[i]);
|
|
DPRINT1("%ws", RootPath);
|
|
|
|
} else if (OptLen == 10 && (i+1 < argc) && ArgMatch(Switch, L"stagepath")) {
|
|
i += 1;
|
|
StagePath = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(StagePath, Argv[i]);
|
|
DPRINT1("%ws", StagePath);
|
|
|
|
} else if (OptLen == 12 && (i+1 < argc) && ArgMatch(Switch, L"workingpath")) {
|
|
i += 1;
|
|
WorkingPath = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(WorkingPath, Argv[i]);
|
|
DPRINT1("%ws", WorkingPath);
|
|
|
|
} else if (OptLen == 10 && (i+1 < argc) && ArgMatch(Switch, L"refdcname")) {
|
|
i += 1;
|
|
RefDCName = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(RefDCName, Argv[i]);
|
|
DPRINT1("%ws", RefDCName);
|
|
|
|
} else if (OptLen == 10 && ArgMatch(Switch, L"createset")) {
|
|
bCreateSet = TRUE;
|
|
++Commands;
|
|
|
|
} else if (OptLen == 10 && ArgMatch(Switch, L"updateset")) {
|
|
bUpdateSet = TRUE;
|
|
++Commands;
|
|
|
|
} else if (OptLen == 7 && ArgMatch(Switch, L"delset")) {
|
|
bDelSet = TRUE;
|
|
++Commands;
|
|
|
|
} else if (OptLen == 13 && ArgMatch(Switch, L"createmember")) {
|
|
bCreateMember = TRUE;
|
|
++Commands;
|
|
|
|
} else if (OptLen == 13 && ArgMatch(Switch, L"updatemember")) {
|
|
bUpdateMember = TRUE;
|
|
++Commands;
|
|
|
|
} else if (OptLen == 10 && ArgMatch(Switch, L"delmember")) {
|
|
bDelMember = TRUE;
|
|
++Commands;
|
|
|
|
} else if (OptLen == 17 && ArgMatch(Switch, L"createsubscriber")) {
|
|
bCreateSubscriber = TRUE;
|
|
++Commands;
|
|
|
|
} else if (OptLen == 17 && ArgMatch(Switch, L"updatesubscriber")) {
|
|
bUpdateSubscriber = TRUE;
|
|
++Commands;
|
|
|
|
} else if (OptLen == 14 && ArgMatch(Switch, L"delsubscriber")) {
|
|
bDelSubscriber = TRUE;
|
|
++Commands;
|
|
|
|
} else if (OptLen == 5 && ArgMatch(Switch, L"dump")) {
|
|
bDump = TRUE;
|
|
++Commands;
|
|
|
|
} else if (OptLen == 4 && ArgMatch(Switch, L"all")) {
|
|
bAffectAll = TRUE;
|
|
|
|
} else if (OptLen == 8 && (i+1 < argc) && ArgMatch(Switch, L"settype")) {
|
|
i += 1;
|
|
ReplicaSetType = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(ReplicaSetType, Argv[i]);
|
|
DPRINT1("%ws", ReplicaSetType);
|
|
|
|
} else if (OptLen == 11 && (i+1 < argc) && ArgMatch(Switch, L"filefilter")) {
|
|
i += 1;
|
|
FileFilter = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(FileFilter, Argv[i]);
|
|
DPRINT1("%ws", FileFilter);
|
|
|
|
} else if (OptLen == 16 && (i+1 < argc) && ArgMatch(Switch, L"directoryfilter")) {
|
|
i += 1;
|
|
DirectoryFilter = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(DirectoryFilter, Argv[i]);
|
|
DPRINT1("%ws", DirectoryFilter);
|
|
|
|
} else if (OptLen == 14 && (i+1 < argc) && ArgMatch(Switch, L"primarymember")) {
|
|
i += 1;
|
|
PrimaryMember = new WCHAR[wcslen(Argv[i])+1];
|
|
wcscpy(PrimaryMember, Argv[i]);
|
|
DPRINT1("%ws", PrimaryMember);
|
|
|
|
} else if (OptLen == 14 && ArgMatch(Switch, L"makemeprimary")) {
|
|
bMakeMePrimary = TRUE;
|
|
|
|
} else if (OptLen == 9 && (i+3 < argc) && ArgMatch(Switch, L"schedule")) {
|
|
|
|
bSchedule = TRUE;
|
|
Interval = _wtoi(Argv[i+1]);
|
|
Stagger = _wtoi(Argv[i+2]);
|
|
Offset = _wtoi(Argv[i+3]);
|
|
i+=3;
|
|
|
|
} else if ((OptLen == 10) && (i+1 < argc) && ArgMatch(Switch, L"schedmask")) {
|
|
|
|
SchedMask = ReadScheduleFile(Argv[i+1]);
|
|
if (SchedMask == NULL) {
|
|
FreeArgv(argc,Argv);
|
|
return MKDSOE_BAD_ARG;
|
|
} else {
|
|
if (bVerboseMode) {
|
|
printf(" schedmask:\n");
|
|
PrintScheduleGrid(SchedMask, 0x0F);
|
|
}
|
|
}
|
|
i+=1;
|
|
|
|
} else if ((OptLen == 14) && (i+1 < argc) && ArgMatch(Switch, L"schedoverride")) {
|
|
|
|
SchedOverride = ReadScheduleFile(Argv[i+1]);
|
|
if (SchedOverride == NULL) {
|
|
FreeArgv(argc,Argv);
|
|
return MKDSOE_BAD_ARG;
|
|
} else {
|
|
if (bVerboseMode) {
|
|
printf(" schedoverride:\n");
|
|
PrintScheduleGrid(SchedOverride, 0x0F);
|
|
}
|
|
}
|
|
i+=1;
|
|
|
|
} else {
|
|
Usage(Argv);
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
|
|
DPRINT0("\n");
|
|
}
|
|
|
|
//
|
|
// Begin of verify command line parameters.
|
|
//
|
|
|
|
if (Commands != 1) {
|
|
if ((Commands == 2) && bDelSubscriber &&bDelMember) {
|
|
//
|
|
// The only time two commands are allowed is when /delsubscriber
|
|
// is used with /delmember
|
|
//
|
|
} else {
|
|
DPRINT0("INVALID ARG: Specify one and only one command except /delmember /delsubscriber.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
}
|
|
|
|
if (NTFRSSettingsDn == NULL) {
|
|
DPRINT0("INVALID ARG: Missing parameter /ntfrssettingsdn.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
|
|
if ((ReplicaSetName == NULL) && !(bDump && bAffectAll)) {
|
|
DPRINT0("INVALID ARG: Missing parameter /setname.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
|
|
if (bCreateSet && ReplicaSetType == NULL) {
|
|
DPRINT0("INVALID ARG: Missing parameter /settype.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
|
|
if (bCreateMember) {
|
|
if (MemberName == NULL) {
|
|
DPRINT0("INVALID ARG: Missing parameter /membername.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
if (bDFSNaming) {
|
|
if (DFSName == NULL) {
|
|
DPRINT0("INVALID ARG: Missing parameter /dfsname.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
Vbar = wcscspn(DFSName, L"|");
|
|
if ((Vbar == wcslen(DFSName)) ||
|
|
(Vbar == 0) ||
|
|
((wcslen(DFSName) - Vbar) < 2)) {
|
|
//
|
|
// No vertical bar found or root or junction parts are zero len.
|
|
//
|
|
DPRINT0("INVALID ARG: Member name must have v-bar with root and junction parts for DFS naming. /dfsname.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bMakeMePrimary && (bCreateSet || bUpdateSet || bDelSet || bDelMember)) {
|
|
DPRINT0("INVALID ARG: /makemeprimary not allowed with /createset, /updateset, /delset, or /delmember.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
|
|
if (bUpdateMember) {
|
|
if ((MemberName == NULL) &&
|
|
(ComputerDn == NULL) &&
|
|
(NT4ComputerName == NULL)) {
|
|
DPRINT0("INVALID ARG: Missing parameter /membername or /computerdn or /computername.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
}
|
|
|
|
if (bCreateSubscriber) {
|
|
if ((MemberName == NULL) &&
|
|
(ComputerDn == NULL) &&
|
|
(NT4ComputerName == NULL)) {
|
|
DPRINT0("INVALID ARG: Missing parameter /membername or /computerdn or /computername.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
|
|
if (RootPath == NULL) {
|
|
DPRINT0("INVALID ARG: Missing parameter /rootpath.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
|
|
if (StagePath == NULL) {
|
|
DPRINT0("INVALID ARG: Missing parameter /stagepath.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
|
|
if (bDFSNaming) {
|
|
if (DFSName == NULL) {
|
|
DPRINT0("INVALID ARG: Missing parameter /dfsname.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
Vbar = wcscspn(DFSName, L"|");
|
|
if ((Vbar == wcslen(DFSName)) ||
|
|
(Vbar == 0) ||
|
|
((wcslen(DFSName) - Vbar) < 2)) {
|
|
//
|
|
// No vertical bar found or root or junction parts are zero len.
|
|
//
|
|
DPRINT0("INVALID ARG: Member name must have v-bar with root and junction parts for DFS naming. /dfsname.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bUpdateSubscriber) {
|
|
if ((MemberName == NULL) &&
|
|
(ComputerDn == NULL) &&
|
|
(NT4ComputerName == NULL)) {
|
|
DPRINT0("INVALID ARG: Missing parameter /membername or /computerdn or /computername.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
}
|
|
|
|
if (bDelMember) {
|
|
if (!bAffectAll &&
|
|
(MemberName == NULL) &&
|
|
(ComputerDn == NULL) &&
|
|
(NT4ComputerName == NULL)) {
|
|
DPRINT0("INVALID ARG: Missing parameter /membername or /computerdn or /computername or /all.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
}
|
|
|
|
if (bDelSubscriber) {
|
|
if (!bDelMember &&
|
|
(MemberName == NULL) &&
|
|
(ComputerDn == NULL) &&
|
|
(NT4ComputerName == NULL)) {
|
|
DPRINT0("INVALID ARG: Missing parameter /membername or /computerdn or /computername.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto ARG_CLEANUP;
|
|
}
|
|
}
|
|
|
|
//
|
|
// End of verify command line parameters.
|
|
//
|
|
|
|
Status = BindToDC(DcName, &pLdap);
|
|
|
|
//
|
|
// Verify that the ntfrs settings DN exists and is actually a NTFRSSettings object.
|
|
//
|
|
i=0;
|
|
ClassFound = FALSE;
|
|
Values = GetValues(pLdap, NTFRSSettingsDn, ATTR_CLASS, TRUE);
|
|
|
|
while (Values && Values[i]) {
|
|
if (_wcsicmp(Values[i],ATTR_NTFRS_SETTINGS) == 0) {
|
|
ClassFound = TRUE;
|
|
break;
|
|
}
|
|
++i;
|
|
}
|
|
LDAP_FREE_VALUES(Values);
|
|
|
|
if (!ClassFound) {
|
|
DPRINT0("INVALID ARG: Check parameter /ntfrssettingsdn.\n");
|
|
Status = MKDSOE_NO_NTFRS_SETTINGS;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Verify that the computer DN exists and is actually a computer object.
|
|
//
|
|
if (ComputerDn != NULL) {
|
|
i=0;
|
|
ClassFound = FALSE;
|
|
Values = GetValues(pLdap, ComputerDn, ATTR_CLASS, TRUE);
|
|
|
|
while (Values && Values[i]) {
|
|
if ((_wcsicmp(Values[i],ATTR_COMPUTER)== 0) ||
|
|
(_wcsicmp(Values[i],ATTR_USER) == 0)) {
|
|
|
|
ClassFound = TRUE;
|
|
break;
|
|
}
|
|
++i;
|
|
}
|
|
LDAP_FREE_VALUES(Values);
|
|
|
|
if (!ClassFound) {
|
|
DPRINT0("INVALID ARG: Check parameter /computerdn.\n");
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
}
|
|
|
|
if (bCreateSet) {
|
|
if (bSchedule) {
|
|
BuildSchedule(&pSchedule, Interval, Stagger, Offset);
|
|
Status = CreateNewReplicaSet(NTFRSSettingsDn,ReplicaSetName,
|
|
ReplicaSetType, FileFilter,
|
|
DirectoryFilter, PrimaryMember, pSchedule);
|
|
} else {
|
|
Status = CreateNewReplicaSet(NTFRSSettingsDn,ReplicaSetName,
|
|
ReplicaSetType, FileFilter,
|
|
DirectoryFilter, PrimaryMember, NULL);
|
|
}
|
|
}
|
|
|
|
if (bUpdateSet) {
|
|
if (bSchedule) {
|
|
BuildSchedule(&pSchedule, Interval, Stagger, Offset);
|
|
Status = UpdateReplicaSet(NTFRSSettingsDn,ReplicaSetName,
|
|
ReplicaSetType, FileFilter,
|
|
DirectoryFilter, PrimaryMember, pSchedule);
|
|
} else {
|
|
Status = UpdateReplicaSet(NTFRSSettingsDn,ReplicaSetName,
|
|
ReplicaSetType, FileFilter,
|
|
DirectoryFilter, PrimaryMember, NULL);
|
|
}
|
|
}
|
|
|
|
if (bCreateMember || bUpdateMember || bCreateSubscriber || bUpdateSubscriber ||
|
|
bDelSubscriber || bDelMember) {
|
|
|
|
//
|
|
// Get the Coumputer Dn using the NT4 account name.
|
|
//
|
|
if ((ComputerDn == NULL) && (NT4ComputerName != NULL)) {
|
|
|
|
if (hDs == INVALID_HANDLE_VALUE) {
|
|
WStatus = DsBind(DcName, NULL, &hDs);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DPRINT2("ERROR: Can not bind to DC (%ws) %d\n", DcName, WStatus);
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
WStatus = DsCrackNames(hDs, // in hDS,
|
|
DS_NAME_NO_FLAGS, // in flags,
|
|
DS_NT4_ACCOUNT_NAME, // in formatOffered,
|
|
DS_FQDN_1779_NAME, // in formatDesired,
|
|
1, // in cNames,
|
|
&NT4ComputerName, // in *rpNames,
|
|
&Cracked); // out *ppResult
|
|
|
|
if ((WStatus == ERROR_SUCCESS) &&
|
|
Cracked && Cracked->cItems && Cracked->rItems &&
|
|
(Cracked->rItems->status == DS_NAME_NO_ERROR)) {
|
|
|
|
ComputerDn = FrsWcsDup(Cracked->rItems->pName);
|
|
DsFreeNameResult(Cracked);
|
|
} else {
|
|
DPRINT1("ERROR: Can not crack computerDn %d\n", WStatus);
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
|
|
bugbug ("we need the domain guid for the DFSRoot's Domain NC which may not be the same as the member")
|
|
|
|
// Get the domain GUID for the domain containing the member.
|
|
//
|
|
if ((ComputerDn != NULL) && bCreateSubscriber) {
|
|
if (hDs == INVALID_HANDLE_VALUE) {
|
|
WStatus = DsBind(DcName, NULL, &hDs);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DPRINT2("ERROR: Can not bind to DC (%ws) %d\n", DcName, WStatus);
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
//
|
|
// First get the DS_CANONICAL_NAME.
|
|
// eg: engineering.widget.com/software/spencer katt
|
|
// Domain-only version includes trailing '/'.
|
|
//
|
|
WStatus = DsCrackNames(hDs, // in hDS,
|
|
DS_NAME_NO_FLAGS, // in flags,
|
|
DS_FQDN_1779_NAME, // in formatOffered,
|
|
DS_CANONICAL_NAME, // in formatDesired,
|
|
1, // in cNames,
|
|
&ComputerDn, // in *rpNames,
|
|
&Cracked); // out *ppResult
|
|
|
|
if ((WStatus == ERROR_SUCCESS) &&
|
|
Cracked && Cracked->cItems && Cracked->rItems &&
|
|
(Cracked->rItems->status == DS_NAME_NO_ERROR)) {
|
|
|
|
//
|
|
// Get the Canonical name and terminate the string after the domain name.
|
|
//
|
|
DomainName = FrsWcsDup(Cracked->rItems->pName);
|
|
DPRINT1("Canonical name: %ws\n", DomainName);
|
|
pw = DomainName;
|
|
while (*pw && (*pw != L'\\')) {
|
|
pw++;
|
|
}
|
|
if (pw != DomainName) {
|
|
*pw = L'\0';
|
|
}
|
|
DPRINT1("Domain name: %ws\n", DomainName);
|
|
DsFreeNameResult(Cracked);
|
|
} else {
|
|
DPRINT2("ERROR: Can not crack canonical name (%ws) %d\n", ComputerDn, WStatus);
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
|
|
//
|
|
// Take the domain name and get the domain GUID.
|
|
//
|
|
WStatus = DsCrackNames(hDs, // in hDS,
|
|
DS_NAME_NO_FLAGS, // in flags,
|
|
DS_CANONICAL_NAME, // in formatOffered,
|
|
DS_UNIQUE_ID_NAME, // in formatDesired,
|
|
1, // in cNames,
|
|
&DomainName, // in *rpNames,
|
|
&Cracked); // out *ppResult
|
|
|
|
if ((WStatus == ERROR_SUCCESS) &&
|
|
Cracked && Cracked->cItems && Cracked->rItems &&
|
|
(Cracked->rItems->status == DS_NAME_NO_ERROR)) {
|
|
|
|
DomainGuidStr = FrsWcsDup(Cracked->rItems->pName);
|
|
DPRINT1("Canonical name: %ws\n", DomainGuidStr);
|
|
DsFreeNameResult(Cracked);
|
|
} else {
|
|
DPRINT2("ERROR: Can not crack canonical DomainName (%ws) %d\n", DomainName, WStatus);
|
|
Status = MKDSOE_BAD_ARG;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// Alternate sequence if above fails.
|
|
|
|
|
|
|
|
DcFlags = DS_DIRECTORY_SERVICE_PREFERRED;
|
|
|
|
do {
|
|
//
|
|
// Get a DC name for a DC in the domain containing this member.
|
|
//
|
|
WStatus = DsGetDcName(NULL, DomainName, NULL, NULL, DcFlags, &pDCInfo);
|
|
if (WStatus != ERROR_SUCCESS) {
|
|
DPRINT1("ERROR: Can not get computer name of DC in member's domain. (%ws)\n", DomainName);
|
|
Status = MKDSOE_BAD_ARG;
|
|
NetApiBufferFree(pDCInfo);
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if ( !(pDCInfo->Flags & DS_DS_FLAG) ) {
|
|
// down level domain
|
|
DPRINT1("ERROR: ComputerDn in down level domains not supported. (%ws)\n", DomainName);
|
|
Status = MKDSOE_BAD_ARG;
|
|
NetApiBufferFree(pDCInfo);
|
|
goto CLEANUP;
|
|
}
|
|
|
|
//
|
|
// Get the domain guid.
|
|
//
|
|
WStatus = DsRoleGetPrimaryDomainInformation(pDCInfo->DomainControllerName,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(PBYTE *)&pBuffer);
|
|
if ((WStatus == RPC_S_SERVER_UNAVAILABLE) && !bRetry) {
|
|
bRetry = TRUE; // only retry once
|
|
NetApiBufferFree(pDCInfo);
|
|
DcFlags |= DS_FORCE_REDISCOVERY;
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
|
|
if (WStatus == ERROR_SUCCESS) {
|
|
|
|
Guid = &pBuffer->DomainGuid;
|
|
//
|
|
// Build stringized Guid in the format returned by the DS.
|
|
// It has an extra dash.
|
|
// e.g. DsCrackNames(hDs,DS_NAME_NO_FLAGS, DS_NT4_ACCOUNT_NAME,
|
|
// DS_UNIQUE_ID_NAME, 1, &NT4ComputerName, &Cracked);
|
|
//
|
|
swprintf(DomainGuidStr,
|
|
L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
|
|
Guid->Data1, Guid->Data2, Guid->Data3,
|
|
Guid->Data4[0], Guid->Data4[1], Guid->Data4[2], Guid->Data4[3],
|
|
Guid->Data4[4], Guid->Data4[5], Guid->Data4[6], Guid->Data4[7]);
|
|
Guid = NULL;
|
|
|
|
DsRoleFreeMemory(pBuffer);
|
|
|
|
NetApiBufferFree(pDCInfo);
|
|
|
|
} else {
|
|
DPRINT1("ERROR: Failed to get domain GUID for computer (%ws)\n", ComputerDn);
|
|
Status = MKDSOE_BAD_ARG;
|
|
NetApiBufferFree(pDCInfo);
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
if (hDs != INVALID_HANDLE_VALUE) {
|
|
DsUnBind(&hDs);
|
|
}
|
|
|
|
//
|
|
// Go get the Guid of the computer object and convert to stringized form
|
|
// that the FRS GUI Snapin will recognize.
|
|
//
|
|
BerValues = GetBerValues(pLdap, ComputerDn, ATTR_OBJECT_GUID, TRUE);
|
|
|
|
if (BerValues && BerValues[0]) {
|
|
// Copy the first value (if any)
|
|
Guid = (GUID *)BerValues[0]->bv_val;
|
|
}
|
|
if (Guid != NULL) {
|
|
//
|
|
// Build stringized Guid in the format returned by the DS.
|
|
// It has an extra dash.
|
|
// e.g. DsCrackNames(hDs,DS_NAME_NO_FLAGS, DS_NT4_ACCOUNT_NAME,
|
|
// DS_UNIQUE_ID_NAME, 1, &NT4ComputerName, &Cracked);
|
|
//
|
|
swprintf(ComputerObjectGuidStr,
|
|
L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
|
|
Guid->Data1, Guid->Data2, Guid->Data3,
|
|
Guid->Data4[0], Guid->Data4[1], Guid->Data4[2], Guid->Data4[3],
|
|
Guid->Data4[4], Guid->Data4[5], Guid->Data4[6], Guid->Data4[7]);
|
|
Guid = NULL;
|
|
} else {
|
|
DPRINT1("INVALID ARG: Cannot get Object Guid for computerdn (%ws).\n", ComputerDn);
|
|
Status = MKDSOE_MEMBER_OBJ_CRE_FAILED;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
// Free ldap's array of BerValues
|
|
LDAP_FREE_BER_VALUES(BerValues);
|
|
|
|
//
|
|
// Check if MemberName is "ComputerObjectGuid". If so then use the
|
|
// stringized Guid to make the FRS UI (in the DFS UI) happy.
|
|
// Else use the MemberName itself.
|
|
//
|
|
if (ArgMatch(MemberName, L"ComputerObjectGuid")) {
|
|
FREE(MemberName);
|
|
MemberName = new WCHAR[wcslen(ComputerObjectGuidStr)+1];
|
|
wcscpy(MemberName, ComputerObjectGuidStr);
|
|
DPRINT1("Member name conversion to GUID: %ws\n", MemberName);
|
|
bDFSNaming = TRUE;
|
|
}
|
|
|
|
if (bCreateMember) {
|
|
Status = CreateNewReplicaMember(NTFRSSettingsDn, ReplicaSetName,
|
|
MemberName, ComputerDn, ServerRef,
|
|
RefDCName);
|
|
}
|
|
|
|
if (bUpdateMember) {
|
|
Status = UpdateReplicaMember(NTFRSSettingsDn, ReplicaSetName,
|
|
MemberName, ComputerDn, ServerRef,
|
|
RefDCName);
|
|
}
|
|
|
|
if (bDelMember) {
|
|
Status = DeleteReplicaMember(NTFRSSettingsDn, ReplicaSetName,
|
|
MemberName,ComputerDn);
|
|
}
|
|
|
|
if (bCreateSubscriber || bUpdateSubscriber) {
|
|
Status = CreateNewSubscriber(NTFRSSettingsDn, ReplicaSetName,
|
|
MemberName, ComputerDn,
|
|
RootPath, StagePath, WorkingPath, RefDCName);
|
|
}
|
|
|
|
if (bDelSubscriber && !bDelMember) {
|
|
Status = DeleteSubscriber(NTFRSSettingsDn, ReplicaSetName,
|
|
MemberName, ComputerDn);
|
|
}
|
|
}
|
|
|
|
if (bDelSet) {
|
|
Status = DeleteReplicaSet(NTFRSSettingsDn, ReplicaSetName);
|
|
}
|
|
|
|
|
|
if (bDump ) {
|
|
DumpReplicaSet(NTFRSSettingsDn, ReplicaSetName);
|
|
}
|
|
|
|
goto CLEANUP;
|
|
|
|
CLEANUP:
|
|
ldap_unbind(pLdap);
|
|
|
|
ARG_CLEANUP:
|
|
|
|
FreeArgv(argc,Argv);
|
|
FREE(MemberName);
|
|
FREE(ReplicaSetName);
|
|
FREE(ComputerDn);
|
|
FREE(NT4ComputerName);
|
|
return Status;
|
|
}
|
|
|