windows-nt/Source/XPSP1/NT/ds/netapi/svcimgs/ntrepl/util/info.c

2919 lines
78 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
info.c
Abstract:
Support the RPC interface that provides internal info to the caller.
Author:
Billy J Fuller 27-Mar-1998
Environment
User mode, winnt32
--*/
#include <ntreppch.h>
#pragma hdrstop
#undef DEBSUB
#define DEBSUB "Info:"
#include <frs.h>
#include <ntdsapi.h>
#include <ntdsapip.h> // ms internal flags for DsCrackNames()
#include <ntfrsapi.h>
#include <info.h>
#include <tablefcn.h>
#include <lmaccess.h>
#include <lmapibuf.h>
#ifdef SECURITY_WIN32
#include <security.h>
#else
#define SECURITY_WIN32
#include <security.h>
#undef SECURITY_WIN32
#endif
extern PCHAR LatestChanges[];
extern PCHAR CoLocationNames[];
//
// Useful macros
//
#define IPRINTGNAME(_I_, _G_, _F_, _GUID_, _P_) \
{ \
if (_G_) { \
GuidToStr(_G_->Guid, _GUID_); \
IPRINT3(_I_, _F_, _P_, _G_->Name, _GUID_); \
} \
}
#define INFO_DN_LEN (80)
extern OSVERSIONINFOEX OsInfo;
extern SYSTEM_INFO SystemInfo;
extern PCHAR ProcessorArchName[10];
//
// DC name for LDAP binding
//
WCHAR InfoDcName[MAX_PATH + 1];
//
// Member Subscriber Links
//
typedef struct _INFO_DN INFO_DN, *PINFO_DN;
struct _INFO_DN {
PINFO_DN Next;
PWCHAR Dn;
PWCHAR SetType;
};
VOID
DbsDisplayRecordIPrint(
IN PTABLE_CTX TableCtx,
IN PINFO_TABLE InfoTable,
IN BOOL Read,
IN PULONG RecordFieldx,
IN ULONG FieldCount
);
//
// From frs\ds.c
//
PVOID *
FrsDsFindValues(
IN PLDAP ldap,
IN PLDAPMessage Entry,
IN PWCHAR DesiredAttr,
IN BOOL DoBerVals
);
PWCHAR
FrsDsExtendDn(
IN PWCHAR Dn,
IN PWCHAR Cn
);
PWCHAR
FrsDsExtendDnOu(
IN PWCHAR Dn,
IN PWCHAR Ou
);
PWCHAR
FrsDsFindValue(
IN PLDAP ldap,
IN PLDAPMessage Entry,
IN PWCHAR DesiredAttr
);
GUID *
FrsDsFindGuid(
IN PLDAP Ldap,
IN PLDAPMessage LdapEntry
);
PWCHAR
FrsDsMakeRdn(
IN PWCHAR DN
);
PWCHAR
FrsDsConvertToSettingsDn(
IN PWCHAR Dn
);
PSCHEDULE
FrsDsFindSchedule(
IN PLDAP Ldap,
IN PLDAPMessage LdapEntry,
OUT PULONG Len
);
VOID
FrsPrintRpcStats(
IN ULONG Severity,
IN PNTFRSAPI_INFO Info, OPTIONAL
IN DWORD Tabs
);
VOID
FrsPrintThreadStats(
IN ULONG Severity,
IN PNTFRSAPI_INFO Info, OPTIONAL
IN DWORD Tabs
);
VOID
InfoPrint(
IN PNTFRSAPI_INFO Info,
IN PCHAR Format,
IN ... )
/*++
Routine Description:
Format and print a line of information output into the info buffer.
Arguments:
Info - Info buffer
Format - printf format
Return Value:
None.
--*/
{
PCHAR Line;
ULONG LineLen;
LONG LineSize;
//
// varargs stuff
//
va_list argptr;
va_start(argptr, Format);
//
// Print the line into the info buffer
//
try {
if (!FlagOn(Info->Flags, NTFRSAPI_INFO_FLAGS_FULL)) {
Line = ((PCHAR)Info) + Info->OffsetToFree;
LineSize = (Info->SizeInChars - (ULONG)(Line - (PCHAR)Info)) - 1;
if (LineSize <= 0 ||
_vsnprintf(Line, LineSize, Format, argptr) < 0) {
SetFlag(Info->Flags, NTFRSAPI_INFO_FLAGS_FULL);
} else {
LineLen = strlen(Line) + 1;
if (Info->CharsToSkip) {
if (LineLen > Info->CharsToSkip) {
Info->CharsToSkip = 0;
} else {
Info->CharsToSkip -= LineLen;
}
} else {
Info->OffsetToFree += LineLen;
Info->TotalChars += LineLen;
}
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
}
va_end(argptr);
}
#define Tab L" "
VOID
InfoTabs(
IN DWORD Tabs,
IN PWCHAR TabW
)
/*++
Routine Description:
Create a string of tabs for prettyprint
Arguments:
Tabs - number of tabs
TabW - preallocated string to receive tabs
Return Value:
Win32 Status
--*/
{
DWORD i;
//
// Adjust indentation
//
Tabs = (Tabs >= MAX_TABS) ? MAX_TABS : Tabs;
for (TabW[0] = L'\0', i = 0; i < Tabs; ++i) {
wcscat(TabW, Tab);
}
}
PWCHAR
InfoDup(
IN PWCHAR StrW,
IN DWORD DesiredLen
)
/*++
Routine Description:
Duplicate up to DesiredLen chars of StrW. DesiredLen includes
the terminating \0.
Arguments:
StrW - original string (untouched)
DesiredLen - Maximum length to duplicate (including terminating \0)
Return Value:
Win32 Status
--*/
{
DWORD Len;
PWCHAR NewStrW;
if (!StrW) {
return NULL;
}
Len = wcslen(StrW) + 1;
if (Len <= DesiredLen) {
return FrsWcsDup(StrW);
}
NewStrW = FrsAlloc(DesiredLen * sizeof(WCHAR));
wcsncpy(NewStrW, StrW, DesiredLen);
NewStrW[DesiredLen - 1] = L'\0';
if (DesiredLen > 4) {
NewStrW[DesiredLen - 2] = L'.';
NewStrW[DesiredLen - 3] = L'.';
NewStrW[DesiredLen - 4] = L'.';
}
return NewStrW;
}
DWORD
InfoPrintDbSets(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs
)
/*++
Routine Description:
Return internal info on replica sets (see private\net\inc\ntfrsapi.h).
Arguments:
Info - RPC output buffer
Tabs
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintDbSets:"
PVOID Key;
PREPLICA Replica;
CHAR Guid[GUID_CHAR_LEN + 1];
WCHAR TabW[MAX_TAB_WCHARS + 1];
extern PGEN_TABLE ReplicasByGuid;
extern PGEN_TABLE DeletedReplicas;
InfoTabs(Tabs, TabW);
IPRINT1(Info, "%wsACTIVE REPLICA SETS\n", TabW);
Key = NULL;
while (Replica = GTabNextDatum(ReplicasByGuid, &Key)) {
if (REPLICA_IS_ACTIVE(Replica)) {
FrsPrintTypeReplica(0, Info, Tabs, Replica, NULL, 0);
} else {
//
// If the replica set is not active, one or more of the GNames
// could have freed guid pointers (feeefeee, bug 319600) so
// don't print the replica set in this case, just the name and state.
//
if (Replica->SetName) {
IPRINT3(Info, "%ws %ws in state %s\n",
TabW, Replica->SetName->Name, RSS_NAME(Replica->ServiceState));
}
}
}
IPRINT0(Info, "\nDELETED REPLICA SETS\n");
Key = NULL;
if (DeletedReplicas) {
while (Replica = GTabNextDatum(DeletedReplicas, &Key)) {
if (Replica->SetName) {
IPRINT2(Info, "%ws %ws\n", TabW, Replica->SetName->Name);
}
}
}
return ERROR_SUCCESS;
}
BOOL
InfoSearch(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs,
IN PLDAP Ldap,
IN PWCHAR Base,
IN ULONG Scope,
IN PWCHAR Filter,
IN PWCHAR Attrs[],
IN ULONG AttrsOnly,
IN LDAPMessage **Res
)
/*++
Routine Description:
Perform ldap_search_s
Arguments:
Info - RPC output buffer
Tabs - number of tabs
Ldap - bound ldap handle
.
.
.
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoSearch:"
DWORD LStatus;
PWCHAR ShortName;
WCHAR TabW[MAX_TAB_WCHARS + 1];
InfoTabs(Tabs, TabW);
LStatus = ldap_search_s(Ldap, Base, Scope, Filter, Attrs, AttrsOnly, Res);
if (LStatus != LDAP_SUCCESS) {
ShortName = InfoDup(Base, 20);
IPRINT5(Info, "%wsWARN - ldap_search_s(%ws, %d, %ws); %ws\n",
TabW, Base, Scope, ComputerName, ldap_err2string(LStatus));
return FALSE;
}
return TRUE;
}
PCONFIG_NODE
InfoAllocBasicNode(
IN PNTFRSAPI_INFO Info,
IN PWCHAR TabW,
IN PWCHAR NodeType,
IN PWCHAR ParentDn,
IN PWCHAR Filter,
IN BOOL *FirstError,
IN PLDAP Ldap,
IN PLDAPMessage LdapEntry
)
/*++
Routine Description:
Allocate a node and fill in the basic info (dn and name)
Arguments:
Info - text buffer
TabW - Prettyprint
NodeType - Prettyprint
Ldap - openned, bound ldap
LdapEntry - returned from ldap_first/next_entry()
Return Value:
NULL if basic info is not available.
--*/
{
#undef DEBSUB
#define DEBSUB "InfoAllocBasicNode:"
PCONFIG_NODE Node = NULL;
PWCHAR ShortName = NULL;
CHAR Guid[GUID_CHAR_LEN + 1];
//
// Fetch values from the DS
//
Node = FrsAllocType(CONFIG_NODE_TYPE);
Node->Dn = FrsDsFindValue(Ldap, LdapEntry, ATTR_DN);
FRS_WCSLWR(Node->Dn);
//
// Name
//
Node->Name = FrsBuildGName(FrsDsFindGuid(Ldap, LdapEntry),
FrsDsMakeRdn(Node->Dn));
if (!Node->Dn || !Node->Name->Name || !Node->Name->Guid) {
IPRINT5(Info, "\n%ws%ws: ERROR - The object returned by the DS"
" lacks a dn (%08x), Rdn (%08x), or Guid(%08x)\n",
TabW, NodeType, Node->Dn, Node->Name->Name, Node->Name->Guid);
if (*FirstError) {
*FirstError = FALSE;
IPRINT5(Info, "%ws%ws: ERROR - Using ldp, bind to %ws and search the "
"container %ws using the filter "
"%ws for more information.\n",
TabW, NodeType, &InfoDcName[2], ParentDn, Filter);
}
return FrsFreeType(Node);
}
IPRINT3(Info, "\n%ws%ws: %ws\n", TabW, NodeType, Node->Name->Name);
ShortName = InfoDup(Node->Dn, INFO_DN_LEN);
IPRINT2(Info, "%ws DN : %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
GuidToStr(Node->Name->Guid, Guid);
IPRINT2(Info, "%ws Guid : %s\n", TabW, Guid);
return Node;
}
VOID
InfoPrintDsCxtions(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs,
IN PLDAP Ldap,
IN PWCHAR Base,
IN BOOL IsSysVol
)
/*++
Routine Description:
Print the cxtions from the DS.
Arguments:
ldap - opened and bound ldap connection
Base - Name of object or container in DS
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintDsCxtions:"
PWCHAR Attrs[7];
PLDAPMessage LdapEntry;
PLDAPMessage LdapMsg = NULL;
PCONFIG_NODE Node = NULL;
PWCHAR ShortName = NULL;
BOOL FirstError = TRUE;
WCHAR TabW[MAX_TAB_WCHARS + 1];
PWCHAR CxtionOptionsWStr = NULL;
//
// Adjust indentation
//
InfoTabs(Tabs, TabW);
//
// Search the DS for cxtions
//
Attrs[0] = ATTR_DN;
Attrs[1] = ATTR_SCHEDULE;
Attrs[2] = ATTR_FROM_SERVER;
Attrs[3] = ATTR_OBJECT_GUID;
Attrs[4] = ATTR_USN_CHANGED;
Attrs[5] = ATTR_ENABLED_CXTION;
Attrs[6] = ATTR_OPTIONS;
Attrs[7] = NULL;
if (!InfoSearch(Info, Tabs, Ldap, Base, LDAP_SCOPE_ONELEVEL,
CATEGORY_CXTION, Attrs, 0, &LdapMsg)) {
goto cleanup;
}
//
// Scan the entries returned from ldap_search
//
for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
LdapEntry != NULL;
LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
//
// Basic info (dn, rdn, and guid)
//
Node = InfoAllocBasicNode(Info, TabW, L"CXTION", Base,
CATEGORY_CXTION, &FirstError, Ldap, LdapEntry);
if (!Node) {
continue;
}
//
// Node's partner's name
//
Node->PartnerDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_FROM_SERVER);
FRS_WCSLWR(Node->PartnerDn);
ShortName = InfoDup(Node->PartnerDn, INFO_DN_LEN);
IPRINT2(Info, "%ws Partner Dn : %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
Node->PartnerName = FrsBuildGName(NULL, FrsDsMakeRdn(Node->PartnerDn));
IPRINT2(Info, "%ws Partner Rdn : %ws\n", TabW, Node->PartnerName->Name);
//
// Enabled
//
Node->EnabledCxtion = FrsDsFindValue(Ldap, LdapEntry, ATTR_ENABLED_CXTION);
IPRINT2(Info, "%ws Enabled : %ws\n", TabW, Node->EnabledCxtion);
//
// Options
//
CxtionOptionsWStr = FrsDsFindValue(Ldap, LdapEntry, ATTR_OPTIONS);
if (CxtionOptionsWStr != NULL) {
Node->CxtionOptions = _wtoi(CxtionOptionsWStr);
CxtionOptionsWStr = FrsFree(CxtionOptionsWStr);
} else {
Node->CxtionOptions = 0;
}
IPRINT2(Info, "%ws Options : 0x%08x\n", TabW, Node->CxtionOptions);
//
// Schedule, if any
//
Node->Schedule = FrsDsFindSchedule(Ldap, LdapEntry, &Node->ScheduleLength);
if (Node->Schedule) {
if (IsSysVol) {
IPRINT1(Info, "%ws IGNORED Schedule\n", TabW);
} else {
IPRINT1(Info, "%ws Schedule\n", TabW);
}
FrsPrintTypeSchedule(0, Info, Tabs + 1, Node->Schedule, NULL, 0);
}
Node = FrsFreeType(Node);
}
cleanup:
LDAP_FREE_MSG(LdapMsg);
FrsFreeType(Node);
FrsFree(ShortName);
}
VOID
InfoCrack(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs,
IN PWCHAR Dn,
IN HANDLE Handle,
IN PWCHAR DomainDnsName,
IN DWORD DesiredFormat
)
/*++
Routine Description:
Find the NT4 account name for Dn. Dn should be the Dn
of a computer object.
Arguments:
Dn - Of computer object
Handle - From DsBind
DomainDnsName - If !NULL, produce new local handle
DesiredFormat - DS_NT4_ACCOUNT_NAME or DS_STRING_SID_NAME
Return Value:
NT4 Account Name or NULL
--*/
{
#undef DEBSUB
#define DEBSUB "InfoCrack:"
DWORD WStatus;
DS_NAME_RESULT *Cracked;
WCHAR TabW[MAX_TAB_WCHARS + 1];
PWCHAR ShortName = NULL;
HANDLE LocalHandle = NULL;
//
// Adjust indentation
//
InfoTabs(Tabs, TabW);
//
// Computer's Dn not available
//
if (!Dn) {
return;
}
//
// Need something to go on!
//
if (!HANDLE_IS_VALID(Handle) && !DomainDnsName) {
return;
}
//
// Bind to Ds
//
if (DomainDnsName) {
WStatus = DsBind(NULL, DomainDnsName, &LocalHandle);
if (!WIN_SUCCESS(WStatus)) {
IPRINT4(Info, "%ws ERROR - DsBind(%ws, %08x); WStatus %s\n",
TabW, DomainDnsName, DesiredFormat, ErrLabelW32(WStatus));
return;
}
Handle = LocalHandle;
}
//
// Crack the computer's distinguished name into its NT4 Account Name
//
WStatus = DsCrackNames(Handle, // in hDS,
DS_NAME_NO_FLAGS, // in flags,
DS_FQDN_1779_NAME, // in formatOffered,
DesiredFormat, // in formatDesired,
1, // in cNames,
&Dn, // in *rpNames,
&Cracked); // out *ppResult
if (!WIN_SUCCESS(WStatus)) {
ShortName = InfoDup(Dn, INFO_DN_LEN);
IPRINT4(Info, "%ws ERROR - DsCrackNames(%ws, %08x); WStatus %s\n",
TabW, ShortName, DesiredFormat, ErrLabelW32(WStatus));
ShortName = FrsFree(ShortName);
//
// What else can we do?
//
if (HANDLE_IS_VALID(LocalHandle)) {
DsUnBind(&LocalHandle);
}
return;
}
//
// Might have it
//
if (Cracked && Cracked->cItems && Cracked->rItems) {
//
// Got it!
//
if (Cracked->rItems->status == DS_NAME_NO_ERROR) {
IPRINT2(Info, "%ws Cracked Domain : %ws\n",
TabW, Cracked->rItems->pDomain);
IPRINT3(Info, "%ws Cracked Name : %08x %ws\n",
TabW, DesiredFormat, Cracked->rItems->pName);
//
// Only got the domain; rebind and try again
//
} else if (Cracked->rItems->status == DS_NAME_ERROR_DOMAIN_ONLY) {
InfoCrack(Info, Tabs, Dn, NULL, Cracked->rItems->pDomain, DesiredFormat);
} else {
ShortName = InfoDup(Dn, INFO_DN_LEN);
IPRINT4(Info, "%ws ERROR - DsCrackNames(%ws, %08x); internal status %d\n",
TabW, ShortName, DesiredFormat, Cracked->rItems->status);
ShortName = FrsFree(ShortName);
}
DsFreeNameResult(Cracked);
} else {
ShortName = InfoDup(Dn, INFO_DN_LEN);
IPRINT3(Info, "%ws ERROR - DsCrackNames(%ws, %08x); no status\n",
TabW, ShortName, DesiredFormat);
ShortName = FrsFree(ShortName);
}
if (HANDLE_IS_VALID(LocalHandle)) {
DsUnBind(&LocalHandle);
}
}
VOID
InfoCrackDns(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs,
IN PLDAP Ldap,
IN PWCHAR Base
)
/*++
Routine Description:
Find the DNS name for Base. Base should be the Dn
of a computer object.
Arguments:
Info
Tabs
Ldap
Base
Return Value:
Prints a message into Info.
--*/
{
#undef DEBSUB
#define DEBSUB "InfoCrackDns:"
PWCHAR Attrs[2];
WCHAR TabW[MAX_TAB_WCHARS + 1];
PLDAPMessage LdapEntry;
PLDAPMessage LdapMsg = NULL;
PWCHAR DnsName = NULL;
//
// Adjust indentation
//
InfoTabs(Tabs, TabW);
//
// Computer's Dn not available
//
if (!Base) {
return;
}
//
// Search the DS for the DNS attribute of Base
//
Attrs[0] = ATTR_DNS_HOST_NAME;
Attrs[1] = NULL;
if (!InfoSearch(Info, Tabs, Ldap, Base, LDAP_SCOPE_BASE,
CATEGORY_ANY, Attrs, 0, &LdapMsg)) {
goto cleanup;
}
LdapEntry = ldap_first_entry(Ldap, LdapMsg);
if (!LdapEntry) {
IPRINT2(Info, "%ws ERROR - No LdapEntry for Dns name on %ws\n", TabW, Base);
goto cleanup;
}
DnsName = FrsDsFindValue(Ldap, LdapEntry, ATTR_DNS_HOST_NAME);
if (!DnsName) {
IPRINT2(Info, "%ws ERROR - No DNS name on %ws\n", TabW, Base);
goto cleanup;
}
//
// Got it!
//
IPRINT2(Info, "%ws Computer's DNS : %ws\n", TabW, DnsName);
cleanup:
LDAP_FREE_MSG(LdapMsg);
FrsFree(DnsName);
}
VOID
InfoPrintMembers(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs,
IN PLDAP Ldap,
IN BOOL IsSysVol,
IN PWCHAR Base,
IN HANDLE DsHandle
)
/*++
Routine Description:
Print the members
Arguments:
ldap - opened and bound ldap connection
Base - Name of object or container in DS
Return Value:
None.
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintMembers:"
PWCHAR Attrs[7];
PLDAPMessage LdapEntry;
PLDAPMessage LdapMsg = NULL;
PCONFIG_NODE Node = NULL;
PWCHAR ShortName = NULL;
BOOL FirstError = TRUE;
WCHAR TabW[MAX_TAB_WCHARS + 1];
//
// Adjust indentation
//
InfoTabs(Tabs, TabW);
//
// Search the DS for members
//
Attrs[0] = ATTR_OBJECT_GUID;
Attrs[1] = ATTR_DN;
Attrs[2] = ATTR_SCHEDULE;
Attrs[3] = ATTR_USN_CHANGED;
Attrs[4] = ATTR_SERVER_REF;
Attrs[5] = ATTR_COMPUTER_REF;
Attrs[6] = NULL;
if (!InfoSearch(Info, Tabs, Ldap, Base, LDAP_SCOPE_ONELEVEL,
CATEGORY_MEMBER, Attrs, 0, &LdapMsg)) {
goto cleanup;
}
//
// Scan the entries returned from ldap_search
//
for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
LdapEntry != NULL;
LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
//
// Basic info (dn, rdn, and guid)
//
Node = InfoAllocBasicNode(Info, TabW, L"MEMBER", Base,
CATEGORY_MEMBER, &FirstError, Ldap, LdapEntry);
if (!Node) {
continue;
}
//
// NTDS Settings (DSA) Reference
//
Node->SettingsDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_SERVER_REF);
ShortName = InfoDup(Node->SettingsDn, INFO_DN_LEN);
IPRINT2(Info, "%ws Server Ref : %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
//
// Computer Reference
//
Node->ComputerDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_COMPUTER_REF);
FRS_WCSLWR(Node->ComputerDn);
ShortName = InfoDup(Node->ComputerDn, INFO_DN_LEN);
IPRINT2(Info, "%ws Computer Ref : %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
InfoCrack(Info, Tabs, Node->ComputerDn, DsHandle, NULL, DS_NT4_ACCOUNT_NAME);
InfoCrack(Info, Tabs, Node->ComputerDn, DsHandle, NULL, DS_STRING_SID_NAME);
InfoCrackDns(Info, Tabs, Ldap, Node->ComputerDn);
//
// Schedule, if any
//
Node->Schedule = FrsDsFindSchedule(Ldap, LdapEntry, &Node->ScheduleLength);
if (Node->Schedule) {
IPRINT1(Info, "%ws Schedule\n", TabW);
FrsPrintTypeSchedule(0, Info, Tabs + 1, Node->Schedule, NULL, 0);
}
//
// Get the inbound cxtions
//
InfoPrintDsCxtions(Info, Tabs + 1, Ldap, Node->Dn, FALSE);
if (IsSysVol) {
if (Node->SettingsDn) {
InfoPrintDsCxtions(Info, Tabs + 1, Ldap, Node->SettingsDn, TRUE);
} else {
IPRINT2(Info, "%ws WARN - %ws lacks a settings reference\n",
TabW, Node->Name->Name);
}
}
Node = FrsFreeType(Node);
}
cleanup:
LDAP_FREE_MSG(LdapMsg);
FrsFreeType(Node);
FrsFree(ShortName);
}
VOID
InfoPrintDsSets(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs,
IN PLDAP Ldap,
IN PWCHAR SetDnAddr,
IN HANDLE DsHandle,
IN OUT PINFO_DN *InfoSets
)
/*++
Routine Description:
Print replica sets from the ds
Arguments:
ldap - opened and bound ldap connection
Base - Name of object or container in DS
Return Value:
None
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintDsSets:"
PWCHAR Attrs[9];
DWORD i;
PINFO_DN InfoSet;
PLDAPMessage LdapEntry;
PLDAPMessage LdapMsg = NULL;
PCONFIG_NODE Node = NULL;
PWCHAR ShortName = NULL;
BOOL FirstError = TRUE;
WCHAR TabW[MAX_TAB_WCHARS + 1];
//
// Adjust indentation
//
InfoTabs(Tabs, TabW);
//
// Have we processed this settings before?
//
for (InfoSet = *InfoSets; InfoSet; InfoSet = InfoSet->Next) {
if (WSTR_EQ(InfoSet->Dn, SetDnAddr)) {
IPRINT2(Info, "%ws %ws processed previously\n", TabW, SetDnAddr);
break;
}
}
//
// Yep; get the sets
//
if (InfoSet) {
//
// Recurse to the next level in the DS hierarchy
//
InfoPrintMembers(Info,
Tabs + 1,
Ldap,
FRS_RSTYPE_IS_SYSVOLW(InfoSet->SetType),
InfoSet->Dn,
DsHandle);
goto cleanup;
}
//
// Search the DS beginning at Base for sets
//
Attrs[0] = ATTR_OBJECT_GUID;
Attrs[1] = ATTR_DN;
Attrs[2] = ATTR_SCHEDULE;
Attrs[3] = ATTR_USN_CHANGED;
Attrs[4] = ATTR_SET_TYPE;
Attrs[5] = ATTR_PRIMARY_MEMBER;
Attrs[6] = ATTR_FILE_FILTER;
Attrs[7] = ATTR_DIRECTORY_FILTER;
Attrs[8] = NULL;
if (!InfoSearch(Info, Tabs, Ldap, SetDnAddr, LDAP_SCOPE_BASE,
CATEGORY_REPLICA_SET, Attrs, 0, &LdapMsg)) {
goto cleanup;
}
//
// Scan the entries returned from ldap_search
//
for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
LdapEntry != NULL;
LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
//
// Basic info (dn, rdn, and guid)
//
Node = InfoAllocBasicNode(Info, TabW, L"SET", SetDnAddr,
CATEGORY_REPLICA_SET, &FirstError, Ldap, LdapEntry);
if (!Node) {
continue;
}
//
// Replica set type
//
Node->SetType = FrsDsFindValue(Ldap, LdapEntry, ATTR_SET_TYPE);
IPRINT2(Info, "%ws Type : %ws\n", TabW, Node->SetType);
//
// Primary member
//
Node->MemberDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_PRIMARY_MEMBER);
ShortName = InfoDup(Node->MemberDn, INFO_DN_LEN);
IPRINT2(Info, "%ws Primary Member: %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
//
// File filter
//
Node->FileFilterList = FrsDsFindValue(Ldap, LdapEntry, ATTR_FILE_FILTER);
IPRINT2(Info, "%ws File Filter : %ws\n", TabW, Node->FileFilterList);
//
// Directory filter
//
Node->DirFilterList = FrsDsFindValue(Ldap, LdapEntry, ATTR_DIRECTORY_FILTER);
IPRINT2(Info, "%ws Dir Filter : %ws\n", TabW, Node->DirFilterList);
//
// Schedule, if any
//
Node->Schedule = FrsDsFindSchedule(Ldap, LdapEntry, &Node->ScheduleLength);
if (Node->Schedule) {
IPRINT1(Info, "%ws Schedule\n", TabW);
FrsPrintTypeSchedule(0, Info, Tabs + 1, Node->Schedule, NULL, 0);
}
InfoSet = FrsAlloc(sizeof(INFO_DN));
InfoSet->Dn = FrsWcsDup(Node->Dn);
InfoSet->SetType = FrsWcsDup(Node->SetType);
InfoSet->Next = *InfoSets;
*InfoSets = InfoSet;
//
// Recurse to the next level in the DS hierarchy
//
InfoPrintMembers(Info,
Tabs + 1,
Ldap,
FRS_RSTYPE_IS_SYSVOLW(Node->SetType),
Node->Dn,
DsHandle);
Node = FrsFreeType(Node);
}
cleanup:
LDAP_FREE_MSG(LdapMsg);
FrsFreeType(Node);
FrsFree(ShortName);
}
VOID
InfoPrintSettings(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs,
IN PLDAP Ldap,
IN PWCHAR MemberDn,
IN HANDLE DsHandle,
IN OUT PINFO_DN *InfoSettings,
IN OUT PINFO_DN *InfoSets
)
/*++
Routine Description:
Scan the DS tree for NTFRS-Settings
Arguments:
ldap - opened and bound ldap connection
Base - Name of object or container in DS
Return Value:
WIN32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintSettings:"
PWCHAR Attrs[5];
PLDAPMessage LdapEntry;
PWCHAR MemberDnAddr;
PWCHAR SetDnAddr;
PWCHAR SettingsDnAddr;
PINFO_DN InfoSetting;
PLDAPMessage LdapMsg = NULL;
PCONFIG_NODE Node = NULL;
PWCHAR ShortName = NULL;
BOOL FirstError = TRUE;
WCHAR TabW[MAX_TAB_WCHARS + 1];
//
// Adjust indentation
//
InfoTabs(Tabs, TabW);
//
// Find the member component
//
MemberDnAddr = wcsstr(MemberDn, L"cn=");
if (!MemberDnAddr) {
IPRINT2(Info, "%ws ERROR - No MemberDnAddr in %ws\n", TabW, MemberDn);
goto cleanup;
}
//
// Find the set component
//
SetDnAddr = wcsstr(MemberDnAddr + 3, L"cn=");
if (!SetDnAddr) {
IPRINT2(Info, "%ws ERROR - No SetDnAddr in %ws\n", TabW, MemberDn);
goto cleanup;
}
//
// Find the settings component
//
SettingsDnAddr = wcsstr(SetDnAddr + 3, L"cn=");
if (!SettingsDnAddr) {
IPRINT2(Info, "%ws ERROR - No SettingsDnAddr in %ws\n", TabW, MemberDn);
goto cleanup;
}
//
// Have we processed this settings before?
//
for (InfoSetting = *InfoSettings; InfoSetting; InfoSetting = InfoSetting->Next) {
if (WSTR_EQ(InfoSetting->Dn, SettingsDnAddr)) {
IPRINT2(Info, "%ws %ws processed previously\n", TabW, SettingsDnAddr);
break;
}
}
//
// Yep; get the sets
//
if (InfoSetting) {
InfoPrintDsSets(Info, Tabs + 1, Ldap, SetDnAddr, DsHandle, InfoSets);
goto cleanup;
}
//
// Search the DS beginning at Base for settings
//
Attrs[0] = ATTR_OBJECT_GUID;
Attrs[1] = ATTR_DN;
Attrs[2] = ATTR_SCHEDULE;
Attrs[3] = ATTR_USN_CHANGED;
Attrs[4] = NULL;
if (!InfoSearch(Info, Tabs, Ldap, SettingsDnAddr, LDAP_SCOPE_BASE,
CATEGORY_NTFRS_SETTINGS, Attrs, 0, &LdapMsg)) {
goto cleanup;
}
//
// Scan the entries returned from ldap_search
//
for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
LdapEntry != NULL;
LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
//
// Basic info (dn, rdn, and guid)
//
Node = InfoAllocBasicNode(Info, TabW, L"SETTINGS", SettingsDnAddr,
CATEGORY_NTFRS_SETTINGS, &FirstError, Ldap, LdapEntry);
if (!Node) {
continue;
}
//
// Schedule, if any
//
Node->Schedule = FrsDsFindSchedule(Ldap, LdapEntry, &Node->ScheduleLength);
if (Node->Schedule) {
IPRINT1(Info, "%ws Schedule\n", TabW);
FrsPrintTypeSchedule(0, Info, Tabs + 1, Node->Schedule, NULL, 0);
}
InfoSetting = FrsAlloc(sizeof(INFO_DN));
InfoSetting->Dn = FrsWcsDup(Node->Dn);
InfoSetting->Next = *InfoSettings;
*InfoSettings = InfoSetting;
//
// Recurse to the next level in the DS hierarchy
//
InfoPrintDsSets(Info, Tabs + 1, Ldap, SetDnAddr, DsHandle, InfoSets);
Node = FrsFreeType(Node);
}
cleanup:
LDAP_FREE_MSG(LdapMsg);
FrsFreeType(Node);
FrsFree(ShortName);
}
VOID
InfoPrintSubscribers(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs,
IN PLDAP Ldap,
IN PWCHAR SubscriptionDn,
IN PINFO_DN *InfoSubs
)
/*++
Routine Description:
Print subscribers
Arguments:
Ldap - opened and bound ldap connection
SubscriptionDn - distininguished name of subscriptions object
Return Value:
WIN32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintSubscribers:"
PWCHAR Attrs[8];
PLDAPMessage LdapEntry;
PINFO_DN InfoSub;
PLDAPMessage LdapMsg = NULL;
PCONFIG_NODE Node = NULL;
PWCHAR ShortName = NULL;
BOOL FirstError = TRUE;
WCHAR TabW[MAX_TAB_WCHARS + 1];
//
// Adjust indentation
//
InfoTabs(Tabs, TabW);
//
// Search the DS beginning at Base for the entries of class "Filter"
//
Attrs[0] = ATTR_OBJECT_GUID;
Attrs[1] = ATTR_DN;
Attrs[2] = ATTR_SCHEDULE;
Attrs[3] = ATTR_USN_CHANGED;
Attrs[4] = ATTR_REPLICA_ROOT;
Attrs[5] = ATTR_REPLICA_STAGE;
Attrs[6] = ATTR_MEMBER_REF;
Attrs[7] = NULL;
if (!InfoSearch(Info, Tabs, Ldap, SubscriptionDn, LDAP_SCOPE_ONELEVEL,
CATEGORY_SUBSCRIBER, Attrs, 0, &LdapMsg)) {
goto cleanup;
}
//
// Scan the entries returned from ldap_search
//
for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
LdapEntry != NULL;
LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
//
// Basic info (dn, rdn, and guid)
//
Node = InfoAllocBasicNode(Info, TabW, L"SUBSCRIBER", SubscriptionDn,
CATEGORY_SUBSCRIBER, &FirstError, Ldap, LdapEntry);
if (!Node) {
continue;
}
//
// Member reference
//
Node->MemberDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_MEMBER_REF);
ShortName = InfoDup(Node->MemberDn, INFO_DN_LEN);
IPRINT2(Info, "%ws Member Ref: %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
if (Node->MemberDn) {
InfoSub = FrsAlloc(sizeof(INFO_DN ));
InfoSub->Dn = FrsWcsDup(Node->MemberDn);
InfoSub->Next = *InfoSubs;
*InfoSubs = InfoSub;
}
//
// Root pathname
//
Node->Root = FrsDsFindValue(Ldap, LdapEntry, ATTR_REPLICA_ROOT);
FRS_WCSLWR(Node->Root);
IPRINT2(Info, "%ws Root : %ws\n", TabW, Node->Root);
//
// Staging pathname
//
Node->Stage = FrsDsFindValue(Ldap, LdapEntry, ATTR_REPLICA_STAGE);
FRS_WCSLWR(Node->Stage);
IPRINT2(Info, "%ws Stage : %ws\n", TabW, Node->Stage);
//
// Schedule, if any
//
Node->Schedule = FrsDsFindSchedule(Ldap, LdapEntry, &Node->ScheduleLength);
if (Node->Schedule) {
IPRINT1(Info, "%ws Schedule\n", TabW);
FrsPrintTypeSchedule(0, Info, Tabs + 1, Node->Schedule, NULL, 0);
}
Node = FrsFreeType(Node);
}
cleanup:
LDAP_FREE_MSG(LdapMsg);
FrsFreeType(Node);
FrsFree(ShortName);
}
VOID
InfoPrintSubscriptions(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs,
IN PLDAP Ldap,
IN PWCHAR ComputerDn,
IN PINFO_DN *InfoSubs
)
/*++
Routine Description:
Recursively scan the DS tree beginning at computer
Arguments:
Info
Tabs
Ldap
ComputerDn
Return Value:
WIN32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintSubscriptions:"
PWCHAR Attrs[6];
PLDAPMessage LdapEntry;
PLDAPMessage LdapMsg = NULL;
PCONFIG_NODE Node = NULL;
PWCHAR ShortName = NULL;
BOOL FirstError = TRUE;
WCHAR TabW[MAX_TAB_WCHARS + 1];
//
// Adjust indentation
//
InfoTabs(Tabs, TabW);
//
// Search the DS beginning at Base for the entries of class "Filter"
//
Attrs[0] = ATTR_OBJECT_GUID;
Attrs[1] = ATTR_DN;
Attrs[2] = ATTR_SCHEDULE;
Attrs[3] = ATTR_USN_CHANGED;
Attrs[4] = ATTR_WORKING;
Attrs[5] = NULL;
if (!InfoSearch(Info, Tabs + 1, Ldap, ComputerDn, LDAP_SCOPE_SUBTREE,
CATEGORY_SUBSCRIPTIONS, Attrs, 0, &LdapMsg)) {
goto cleanup;
}
//
// Scan the entries returned from ldap_search
//
for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
LdapEntry != NULL;
LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
//
// Basic info (dn, rdn, and guid)
//
Node = InfoAllocBasicNode(Info, TabW, L"SUBSCRIPTION", ComputerDn,
CATEGORY_SUBSCRIPTIONS, &FirstError, Ldap, LdapEntry);
if (!Node) {
continue;
}
//
// Working Directory
//
Node->Working = FrsDsFindValue(Ldap, LdapEntry, ATTR_WORKING);
IPRINT2(Info, "%ws Working : %ws\n", TabW, Node->Working);
IPRINT2(Info, "%ws Actual Working: %ws\n", TabW, WorkingPath);
//
// Schedule, if any
//
Node->Schedule = FrsDsFindSchedule(Ldap, LdapEntry, &Node->ScheduleLength);
if (Node->Schedule) {
IPRINT1(Info, "%ws Schedule\n", TabW);
FrsPrintTypeSchedule(0, Info, Tabs + 1, Node->Schedule, NULL, 0);
}
//
// Recurse to the next level in the DS hierarchy
//
InfoPrintSubscribers(Info, Tabs + 1, Ldap, Node->Dn, InfoSubs);
Node = FrsFreeType(Node);
}
cleanup:
LDAP_FREE_MSG(LdapMsg);
FrsFreeType(Node);
FrsFree(ShortName);
}
BOOL
InfoPrintComputer(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs,
IN PLDAP Ldap,
IN PWCHAR FindDn,
IN PWCHAR ObjectCategory,
IN ULONG Scope,
OUT PINFO_DN *InfoSubs
)
/*++
Routine Description:
Return internal info on DS computer objects.
Arguments:
Info - RPC output buffer
Tabs - number of tabs
Ldap - bound ldap handle
DefaultNcDn - DN of the DCs default naming context
FindDn - Base Dn for search
ObjectCategory - Object class (computer or user)
A user object serves the same purpose as the computer
object *sometimes* following a NT4 to NT5 upgrade.
Scope - Scope of search (currently BASE or SUBTREE)
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintComputer:"
DWORD i;
DWORD LStatus;
PLDAPMessage LdapEntry;
PWCHAR UserAccountControl;
DWORD NumVals;
PINFO_DN InfoSub;
BOOL FoundAComputer = FALSE;
PWCHAR ShortName = NULL;
PCONFIG_NODE Node = NULL;
PLDAPMessage LdapMsg = NULL;
PWCHAR *Values = NULL;
DWORD WStatus = ERROR_SUCCESS;
BOOL FirstError = TRUE;
WCHAR TabW[MAX_TAB_WCHARS + 1];
WCHAR Filter[MAX_PATH + 1];
WCHAR ComputerFqdn[MAX_PATH + 1];
DWORD ComputerFqdnLen;
PWCHAR Attrs[10];
//
// Initialize return value
//
*InfoSubs = NULL;
//
// Adjust indentation
//
InfoTabs(Tabs, TabW);
//
// Filter that locates our computer object
//
swprintf(Filter, L"(&%s(sAMAccountName=%s$))", ObjectCategory, ComputerName);
//
// Search the DS beginning at Base for the entries of class "Filter"
//
Attrs[0] = ATTR_OBJECT_GUID;
Attrs[1] = ATTR_DN;
Attrs[2] = ATTR_SCHEDULE;
Attrs[3] = ATTR_COMPUTER_REF_BL;
Attrs[4] = ATTR_SERVER_REF;
Attrs[5] = ATTR_SERVER_REF_BL;
Attrs[6] = ATTR_USER_ACCOUNT_CONTROL;
Attrs[7] = ATTR_DNS_HOST_NAME;
Attrs[8] = NULL;
InfoSearch(Info,
Tabs + 1,
Ldap,
FindDn,
Scope,
Filter,
Attrs,
0,
&LdapMsg);
if (!LdapMsg) {
goto CLEANUP;
}
//
// Scan the entries returned from ldap_search
//
for (LdapEntry = ldap_first_entry(Ldap, LdapMsg);
LdapEntry != NULL && WIN_SUCCESS(WStatus);
LdapEntry = ldap_next_entry(Ldap, LdapEntry)) {
FoundAComputer = TRUE;
//
// Basic info (dn, rdn, and guid)
//
Node = InfoAllocBasicNode(Info,
TabW,
L"COMPUTER",
FindDn,
Filter,
&FirstError,
Ldap,
LdapEntry);
if (!Node) {
continue;
}
UserAccountControl = FrsDsFindValue(Ldap, LdapEntry, ATTR_USER_ACCOUNT_CONTROL);
if (UserAccountControl) {
IPRINT2(Info, "%ws UAC : 0x%08x\n",
TabW, wcstoul(UserAccountControl, NULL, 10));
UserAccountControl = FrsFree(UserAccountControl);
}
//
// Server reference
//
Node->SettingsDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_SERVER_REF_BL);
ShortName = InfoDup(Node->SettingsDn, INFO_DN_LEN);
IPRINT2(Info, "%ws Server BL : %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
if (!Node->SettingsDn) {
Node->SettingsDn = FrsDsFindValue(Ldap, LdapEntry, ATTR_SERVER_REF);
ShortName = InfoDup(Node->SettingsDn, INFO_DN_LEN);
IPRINT2(Info, "%ws Server Ref: %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
}
//
// Make sure it references the settings; not the server
//
Node->SettingsDn = FrsDsConvertToSettingsDn(Node->SettingsDn);
ShortName = InfoDup(Node->SettingsDn, INFO_DN_LEN);
IPRINT2(Info, "%ws Settings : %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
//
// DNS Host Name
//
Node->DnsName = FrsDsFindValue(Ldap, LdapEntry, ATTR_DNS_HOST_NAME);
IPRINT2(Info, "%ws DNS Name : %ws\n", TabW, Node->DnsName);
//
// Schedule, if any
//
Node->Schedule = FrsDsFindSchedule(Ldap, LdapEntry, &Node->ScheduleLength);
if (Node->Schedule) {
IPRINT1(Info, "%ws Schedule\n", TabW);
FrsPrintTypeSchedule(0, Info, Tabs + 1, Node->Schedule, NULL, 0);
}
InfoPrintSubscriptions(Info, Tabs + 1, Ldap, Node->Dn, InfoSubs);
//
// Subscriber Member Bls
//
if (!*InfoSubs) {
IPRINT2(Info, "%ws %ws IS NOT A MEMBER OF ANY SET!\n",
TabW, ComputerName);
} else {
IPRINT1(Info, "%ws Subscriber Member Back Links:\n", TabW);
for (InfoSub = *InfoSubs; InfoSub; InfoSub = InfoSub->Next) {
FRS_WCSLWR(InfoSub->Dn);
ShortName = InfoDup(InfoSub->Dn, INFO_DN_LEN);
IPRINT2(Info, "%ws %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
}
}
//
// Next computer
//
Node = FrsFreeType(Node);
}
CLEANUP:
LDAP_FREE_MSG(LdapMsg);
FrsFreeType(Node);
FrsFree(ShortName);
return FoundAComputer;
}
DWORD
InfoPrintDs(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs
)
/*++
Routine Description:
Return internal info on DS (see private\net\inc\ntfrsapi.h).
Arguments:
Info - RPC output buffer
Tabs - number of tabs
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintDs:"
DWORD WStatus;
DWORD LStatus;
DWORD i;
PWCHAR DcAddr;
PWCHAR DcName;
PWCHAR DcDnsName;
DWORD NumVals;
PWCHAR Config;
PLDAPMessage LdapEntry;
BOOL PrintedComputers;
PINFO_DN InfoSub;
PINFO_DN InfoSetting;
PINFO_DN InfoSet;
PINFO_DN InfoSubs = NULL;
PINFO_DN InfoSettings = NULL;
PINFO_DN InfoSets = NULL;
PWCHAR ShortName = NULL;
PWCHAR SitesDn = NULL;
PWCHAR ServicesDn = NULL;
PWCHAR DefaultNcDn = NULL;
PWCHAR ComputersDn = NULL;
PWCHAR DomainControllersDn = NULL;
PLDAPMessage LdapMsg = NULL;
PWCHAR *Values = NULL;
PLDAP Ldap = NULL;
HANDLE DsHandle = INVALID_HANDLE_VALUE;
WCHAR ComputerFqdn[MAX_PATH + 1];
DWORD ComputerFqdnLen;
WCHAR TabW[MAX_TAB_WCHARS + 1];
CHAR Guid[GUID_CHAR_LEN + 1];
PWCHAR Attrs[3];
PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
struct l_timeval Timeout;
DWORD InfoFlags;
CHAR FlagBuffer[220];
ULONG ulOptions;
extern PWCHAR DsDomainControllerName;
extern FLAG_NAME_TABLE DsGetDcInfoFlagNameTable[];
//
// Client side ldap_connect timeout in seconds. Reg value "Ldap Bind Timeout In Seconds". Default is 30 seconds.
//
extern DWORD LdapBindTimeoutInSeconds;
//
// Adjust indentation
//
InfoTabs(Tabs, TabW);
IPRINT1(Info, "%wsNTFRS CONFIGURATION IN THE DS\n", TabW);
Ldap = NULL;
if (IsADc) {
DcAddr = NULL;
DcName = ComputerName;
DcDnsName = ComputerDnsName;
IPRINT1(Info, "%wsSUBSTITUTE DCINFO FOR DC\n", TabW);
IPRINT2(Info, "%ws FRS DomainControllerName: %ws\n", TabW, DsDomainControllerName);
IPRINT2(Info, "%ws Computer Name : %ws\n", TabW, DcName);
IPRINT2(Info, "%ws Computer DNS Name : %ws\n", TabW, DcDnsName);
} else {
//
// Domain Controller
//
WStatus = DsGetDcName(NULL,
NULL,
NULL,
NULL,
DS_DIRECTORY_SERVICE_REQUIRED |
DS_WRITABLE_REQUIRED |
DS_BACKGROUND_ONLY,
&DcInfo);
if (!WIN_SUCCESS(WStatus)) {
DcInfo = NULL;
IPRINT2(Info, "%wsWARN - DsGetDcName WStatus %s; Flushing cache...\n",
TabW, ErrLabelW32(WStatus));
WStatus = DsGetDcName(NULL,
NULL,
NULL,
NULL,
DS_DIRECTORY_SERVICE_REQUIRED |
DS_WRITABLE_REQUIRED |
DS_FORCE_REDISCOVERY,
&DcInfo);
}
//
// Report the error and retry for any DC
//
if (!WIN_SUCCESS(WStatus)) {
DcInfo = NULL;
IPRINT3(Info, "%wsERROR - DsGetDcName(%ws); WStatus %s\n",
TabW, ComputerName, ErrLabelW32(WStatus));
goto cleanup;
}
//
// Dump dcinfo
//
IPRINT1(Info, "%wsDCINFO\n", TabW);
IPRINT2(Info, "%ws LAST DomainControllerName: %ws\n", TabW, DsDomainControllerName);
IPRINT2(Info, "%ws DomainControllerName : %ws\n", TabW, DcInfo->DomainControllerName);
IPRINT2(Info, "%ws DomainControllerAddress : %ws\n", TabW, DcInfo->DomainControllerAddress);
IPRINT2(Info, "%ws DomainControllerType : %08x\n",TabW, DcInfo->DomainControllerAddressType);
IPRINT2(Info, "%ws DomainName : %ws\n", TabW, DcInfo->DomainName);
IPRINT2(Info, "%ws DnsForestName : %ws\n", TabW, DcInfo->DnsForestName);
IPRINT2(Info, "%ws DcSiteName : %ws\n", TabW, DcInfo->DcSiteName);
IPRINT2(Info, "%ws ClientSiteName : %ws\n", TabW, DcInfo->ClientSiteName);
InfoFlags = DcInfo->Flags;
FrsFlagsToStr(InfoFlags, DsGetDcInfoFlagNameTable, sizeof(FlagBuffer), FlagBuffer);
IPRINT3(Info, "%ws Flags : %08x [%s]\n",TabW, InfoFlags, FlagBuffer);
if (!DsDomainControllerName ||
!DcInfo->DomainControllerName ||
WSTR_NE(DsDomainControllerName, DcInfo->DomainControllerName)) {
IPRINT3(Info, "%wsWARN - Using DC %ws; not %ws\n",
TabW, DcInfo->DomainControllerName, DsDomainControllerName);
}
//
// Binding address
//
DcAddr = DcInfo->DomainControllerAddress;
DcDnsName = DcInfo->DomainControllerName;
}
wcsncpy(InfoDcName, DcDnsName, ARRAY_SZ(InfoDcName)-1);
//
// BIND to the DS
//
IPRINT1(Info, "\n%wsBINDING TO THE DS:\n", TabW);
//
// 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.
//
//
// Remove the leading \\ if they exist.
//
FRS_TRIM_LEADING_2SLASH(DcDnsName);
FRS_TRIM_LEADING_2SLASH(DcAddr);
ulOptions = PtrToUlong(LDAP_OPT_ON);
Timeout.tv_sec = LdapBindTimeoutInSeconds;
Timeout.tv_usec = 0;
//
// Try using DcDnsName first.
//
if ((Ldap == NULL) && (DcDnsName != NULL)) {
Ldap = ldap_init(DcDnsName, LDAP_PORT);
if (Ldap != NULL) {
ldap_set_option(Ldap, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
LStatus = ldap_connect(Ldap, &Timeout);
if (LStatus != LDAP_SUCCESS) {
IPRINT4(Info, "%ws WARN - ldap_connect(%ws); (ldap error %08x = %ws)\n",
TabW, DcDnsName, LStatus, ldap_err2string(LStatus));
ldap_unbind_s(Ldap);
Ldap = NULL;
} else {
IPRINT2(Info, "%ws ldap_connect : %ws\n", TabW, DcDnsName);
}
}
}
//
// Try using DcAddr next.
//
if ((Ldap == NULL) && (DcAddr != NULL)) {
Ldap = ldap_init(DcAddr, LDAP_PORT);
if (Ldap != NULL) {
ldap_set_option(Ldap, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
LStatus = ldap_connect(Ldap, &Timeout);
if (LStatus != LDAP_SUCCESS) {
IPRINT4(Info, "%ws WARN - ldap_connect(%ws); (ldap error %08x = %ws)\n",
TabW, DcAddr, LStatus, ldap_err2string(LStatus));
ldap_unbind_s(Ldap);
Ldap = NULL;
} else {
IPRINT2(Info, "%ws ldap_connect : %ws\n", TabW, DcAddr);
}
}
}
//
// Try using DcName finally.
//
if ((Ldap == NULL) && (DcName != NULL)) {
Ldap = ldap_init(DcName, LDAP_PORT);
if (Ldap != NULL) {
ldap_set_option(Ldap, LDAP_OPT_AREC_EXCLUSIVE, &ulOptions);
LStatus = ldap_connect(Ldap, &Timeout);
if (LStatus != LDAP_SUCCESS) {
IPRINT4(Info, "%ws WARN - ldap_connect(%ws); (ldap error %08x = %ws)\n",
TabW, DcName, LStatus, ldap_err2string(LStatus));
ldap_unbind_s(Ldap);
Ldap = NULL;
} else {
IPRINT2(Info, "%ws ldap_connect : %ws\n", TabW, DcName);
}
}
}
//
// Whatever it is, we can't find it.
//
if (!Ldap) {
IPRINT6(Info, "%ws ERROR - ldap_connect(DNS %ws, BIOS %ws, IP %ws); (ldap error %08x = %ws)\n",
TabW, DcDnsName, DcName, DcAddr, LStatus, ldap_err2string(LStatus));
goto cleanup;
}
//
// Bind to the ldap server
//
LStatus = ldap_bind_s(Ldap, NULL, NULL, LDAP_AUTH_NEGOTIATE);
//
// No luck; report error and carry on
//
if (LStatus != LDAP_SUCCESS) {
IPRINT4(Info, "%ws ERROR - ldap_bind_s(%ws); (ldap error %08x = %ws)\n",
TabW, ComputerName, LStatus, ldap_err2string(LStatus));
goto cleanup;
}
//
// Bind to the Ds (for various Ds calls such as DsCrackName())
//
//
// DC's Dns Name
//
WStatus = ERROR_RETRY;
if (!WIN_SUCCESS(WStatus) && DcDnsName) {
WStatus = DsBind(DcDnsName, NULL, &DsHandle);
if (!WIN_SUCCESS(WStatus)) {
DsHandle = NULL;
IPRINT3(Info, "%ws WARN - DsBind(DcDnsName %ws); WStatus %s\n",
TabW, DcDnsName, ErrLabelW32(WStatus));
} else {
IPRINT2(Info, "%ws DsBind : %ws\n", TabW, DcDnsName);
}
}
//
// DC's Computer Name
//
if (!WIN_SUCCESS(WStatus) && DcName) {
WStatus = DsBind(DcName, NULL, &DsHandle);
if (!WIN_SUCCESS(WStatus)) {
DsHandle = NULL;
IPRINT3(Info, "%ws WARN - DsBind(DcName %ws); WStatus %s\n",
TabW, DcName, ErrLabelW32(WStatus));
} else {
IPRINT2(Info, "%ws DsBind : %ws\n", TabW, DcName);
}
}
//
// DC's IP Address
//
if (!WIN_SUCCESS(WStatus) && DcAddr) {
WStatus = DsBind(DcAddr, NULL, &DsHandle);
if (!WIN_SUCCESS(WStatus)) {
DsHandle = NULL;
IPRINT3(Info, "%ws WARN - DsBind(DcAddr %ws); WStatus %s\n",
TabW, DcAddr, ErrLabelW32(WStatus));
} else {
IPRINT2(Info, "%ws DsBind : %ws\n", TabW, DcAddr);
}
}
//
// Whatever it is, we can't find it
//
if (!WIN_SUCCESS(WStatus)) {
IPRINT5(Info, "%ws ERROR - DsBind(DNS %ws, BIOS %ws, IP %ws); WStatus %s\n",
TabW, DcDnsName, DcName, DcAddr, ErrLabelW32(WStatus));
goto cleanup;
}
//
// NAMING CONTEXTS
//
IPRINT1(Info, "\n%wsNAMING CONTEXTS:\n", TabW);
//
// Find the naming contexts and the default naming context
//
Attrs[0] = ATTR_NAMING_CONTEXTS;
Attrs[1] = ATTR_DEFAULT_NAMING_CONTEXT;
Attrs[2] = NULL;
if (!InfoSearch(Info, Tabs + 1, Ldap, CN_ROOT, LDAP_SCOPE_BASE, CATEGORY_ANY,
Attrs, 0, &LdapMsg)) {
goto cleanup;
}
LdapEntry = ldap_first_entry(Ldap, LdapMsg);
if (!LdapEntry) {
IPRINT2(Info, "%ws ERROR - ldap_first_entry(contexts, %ws) no entry\n",
TabW, ComputerName);
goto cleanup;
}
Values = (PWCHAR *)FrsDsFindValues(Ldap, LdapEntry, ATTR_NAMING_CONTEXTS, FALSE);
if (!Values) {
IPRINT2(Info, "%ws ERROR - FrsDsFindValues(contexts, %ws) no entry\n",
TabW, ComputerName);
goto cleanup;
}
//
// Now, find the naming context that begins with "CN=configuration"
//
NumVals = ldap_count_values(Values);
while (NumVals--) {
FRS_WCSLWR(Values[NumVals]);
Config = wcsstr(Values[NumVals], CONFIG_NAMING_CONTEXT);
if (Config && Config == Values[NumVals]) {
//
// Build the pathname for "configuration\sites & services"
//
SitesDn = FrsDsExtendDn(Config, CN_SITES);
ServicesDn = FrsDsExtendDn(Config, CN_SERVICES);
break;
}
}
LDAP_FREE_VALUES(Values);
//
// Finally, find the default naming context
//
Values = (PWCHAR *)FrsDsFindValues(Ldap,
LdapEntry,
ATTR_DEFAULT_NAMING_CONTEXT,
FALSE);
if (!Values) {
IPRINT2(Info, "%ws ERROR - FrsDsFindValues(default naming context, %ws) no entry\n",
TabW, ComputerName);
goto cleanup;
}
DefaultNcDn = FrsWcsDup(Values[0]);
ComputersDn = FrsDsExtendDn(DefaultNcDn, CN_COMPUTERS);
DomainControllersDn = FrsDsExtendDnOu(DefaultNcDn, CN_DOMAIN_CONTROLLERS);
LDAP_FREE_VALUES(Values);
ShortName = InfoDup(SitesDn, INFO_DN_LEN);
IPRINT2(Info, "%ws SitesDn : %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
ShortName = InfoDup(ServicesDn, INFO_DN_LEN);
IPRINT2(Info, "%ws ServicesDn : %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
ShortName = InfoDup(DefaultNcDn, INFO_DN_LEN);
IPRINT2(Info, "%ws DefaultNcDn: %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
ShortName = InfoDup(ComputersDn, INFO_DN_LEN);
IPRINT2(Info, "%ws ComputersDn: %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
ShortName = InfoDup(DomainControllersDn, INFO_DN_LEN);
IPRINT2(Info, "%ws DomainCtlDn: %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
//
// Retrieve the computer's fully qualified Dn
//
ComputerFqdnLen = MAX_PATH;
if (!GetComputerObjectName(NameFullyQualifiedDN, ComputerFqdn, &ComputerFqdnLen)) {
IPRINT4(Info, "%ws ERROR - GetComputerObjectName(%ws); Len %d, WStatus %s\n",
TabW, ComputerName, ComputerFqdnLen, ErrLabelW32(GetLastError()));
ComputerFqdn[0] = L'\0';
} else {
ShortName = InfoDup(ComputerFqdn, INFO_DN_LEN);
IPRINT2(Info, "%ws Fqdn : %ws\n", TabW, ShortName);
ShortName = FrsFree(ShortName);
}
//
// Find and print the computer info
//
PrintedComputers = FALSE;
if (!PrintedComputers && ComputerFqdn[0]) {
IPRINT1(Info, "%ws Searching : Fqdn\n", TabW);
PrintedComputers = InfoPrintComputer(Info, Tabs, Ldap, ComputerFqdn,
CATEGORY_COMPUTER, LDAP_SCOPE_BASE, &InfoSubs);
}
if (!PrintedComputers && ComputersDn) {
IPRINT1(Info, "%ws Searching : Computers\n", TabW);
PrintedComputers = InfoPrintComputer(Info, Tabs, Ldap, ComputersDn,
CATEGORY_COMPUTER, LDAP_SCOPE_SUBTREE, &InfoSubs);
}
if (!PrintedComputers && DomainControllersDn) {
IPRINT1(Info, "%ws Searching : Domain Controllers\n", TabW);
PrintedComputers = InfoPrintComputer(Info, Tabs, Ldap, DomainControllersDn,
CATEGORY_COMPUTER, LDAP_SCOPE_SUBTREE, &InfoSubs);
}
if (!PrintedComputers && DefaultNcDn) {
IPRINT1(Info, "%ws Searching : Default Naming Context\n", TabW);
PrintedComputers = InfoPrintComputer(Info, Tabs, Ldap, DefaultNcDn,
CATEGORY_COMPUTER, LDAP_SCOPE_SUBTREE, &InfoSubs);
}
if (!PrintedComputers && DefaultNcDn) {
IPRINT1(Info, "%ws Searching : Default Naming Context for USER\n", TabW);
PrintedComputers = InfoPrintComputer(Info, Tabs, Ldap, DefaultNcDn,
CATEGORY_USER, LDAP_SCOPE_SUBTREE, &InfoSubs);
}
for (InfoSub = InfoSubs; InfoSub; InfoSub = InfoSub->Next) {
InfoPrintSettings(Info, Tabs, Ldap, InfoSub->Dn, DsHandle, &InfoSettings,
&InfoSets);
}
cleanup:
//
// Cleanup
//
LDAP_FREE_VALUES(Values);
LDAP_FREE_MSG(LdapMsg);
if (DcInfo) {
NetApiBufferFree(DcInfo);
DcInfo = NULL;
}
if (Ldap) {
ldap_unbind_s(Ldap);
}
if (HANDLE_IS_VALID(DsHandle)) {
DsUnBind(&DsHandle);
}
FrsFree(SitesDn);
FrsFree(ServicesDn);
FrsFree(DefaultNcDn);
FrsFree(ComputersDn);
FrsFree(DomainControllersDn);
FrsFree(ShortName);
while (InfoSub = InfoSubs) {
InfoSubs = InfoSub->Next;
FrsFree(InfoSub->Dn);
FrsFree(InfoSub->SetType);
FrsFree(InfoSub);
}
while (InfoSetting = InfoSettings) {
InfoSettings = InfoSetting->Next;
FrsFree(InfoSetting->Dn);
FrsFree(InfoSetting->SetType);
FrsFree(InfoSetting);
}
while (InfoSet = InfoSets) {
InfoSets = InfoSet->Next;
FrsFree(InfoSet->Dn);
FrsFree(InfoSet->SetType);
FrsFree(InfoSet);
}
//
// Real error messages are in the info buffer
//
return ERROR_SUCCESS;
}
PVOID
InfoFreeInfoTable(
IN PINFO_TABLE InfoTable,
IN PNTFRSAPI_INFO Info
)
/*++
Routine Description:
Free the info IDTable
Arguments:
InfoTable
Info
Return Value:
NULL
--*/
{
#undef DEBSUB
#define DEBSUB "InfoFreeInfoTable:"
JET_ERR jerr;
if (!InfoTable) {
return NULL;
}
if (InfoTable->TableCtx) {
DbsFreeTableContext(InfoTable->TableCtx, InfoTable->ThreadCtx->JSesid);
}
if (InfoTable->ThreadCtx) {
jerr = DbsCloseJetSession(InfoTable->ThreadCtx);
if (!JET_SUCCESS(jerr)) {
IPRINT1(Info, "DbsCloseJetSession jet error = %s\n", ErrLabelJet(jerr));
}
InfoTable->ThreadCtx = FrsFreeType(InfoTable->ThreadCtx);
}
return FrsFree(InfoTable);
}
JET_ERR
InfoConfigTableWorker(
IN PTHREAD_CTX ThreadCtx,
IN PTABLE_CTX TableCtx,
IN PCONFIG_TABLE_RECORD ConfigRecord,
IN PINFO_TABLE InfoTable
)
/*++
Routine Description:
This is a worker function passed to FrsEnumerateTable(). Each time
it is called it prints an entry into the info buffer.
Arguments:
ThreadCtx - Needed to access Jet.
TableCtx - A ptr to an ConfigTable context struct.
ConfigRecord - A ptr to a config table record.
InfoTable
Thread Return Value:
A Jet error status. Success means call us with the next record.
Failure means don't call again and pass our status back to the
caller of FrsEnumerateTable().
--*/
{
#undef DEBSUB
#define DEBSUB "InfoConfigTableWorker:"
if (FlagOn(InfoTable->Info->Flags, NTFRSAPI_INFO_FLAGS_FULL)) {
return JET_errNoCurrentRecord;
}
IPRINT0(InfoTable->Info, "\n\n");
DbsDisplayRecordIPrint(TableCtx, InfoTable, TRUE, NULL, 0);
return JET_errSuccess;
}
JET_ERR
InfoIDTableWorker(
IN PTHREAD_CTX ThreadCtx,
IN PTABLE_CTX TableCtx,
IN PIDTABLE_RECORD IDTableRec,
IN PINFO_TABLE InfoTable
)
/*++
Routine Description:
This is a worker function passed to FrsEnumerateTable(). Each time
it is called it prints an entry into the info buffer.
Arguments:
ThreadCtx - Needed to access Jet.
TableCtx - A ptr to an IDTable context struct.
IDTableRec - A ptr to a IDTable record.
InfoTable
Thread Return Value:
A Jet error status. Success means call us with the next record.
Failure means don't call again and pass our status back to the
caller of FrsEnumerateTable().
--*/
{
#undef DEBSUB
#define DEBSUB "InfoIDTableWorker:"
CHAR FlagBuffer[120];
WCHAR TabW[MAX_TAB_WCHARS + 1];
CHAR TimeStr[TIME_STRING_LENGTH];
CHAR Guid[GUID_CHAR_LEN + 1];
if (FlagOn(InfoTable->Info->Flags, NTFRSAPI_INFO_FLAGS_FULL)) {
return JET_errNoCurrentRecord;
}
//
// Table Descriptor
//
IPRINT1(InfoTable->Info, "\nTable Type: ID Table for %ws\n",
InfoTable->Replica->ReplicaName->Name);
DbsDisplayRecordIPrint(TableCtx, InfoTable, TRUE, NULL, 0);
return JET_errSuccess;
}
JET_ERR
InfoInOutLogTableWorker(
IN PTHREAD_CTX ThreadCtx,
IN PTABLE_CTX TableCtx,
IN PCHANGE_ORDER_COMMAND Coc,
IN PINFO_TABLE InfoTable,
IN PWCHAR TableDescriptor
)
/*++
Routine Description:
This is a worker function passed to FrsEnumerateTable(). Each time
it is called it prints an entry into the info buffer.
Arguments:
ThreadCtx - Needed to access Jet.
TableCtx - A ptr to an IDTable context struct.
Coc - A ptr to a inbound log record (change order)
InfoTable
TableDescriptor
Thread Return Value:
A Jet error status. Success means call us with the next record.
Failure means don't call again and pass our status back to the
caller of FrsEnumerateTable().
--*/
{
#undef DEBSUB
#define DEBSUB "InfoInOutLogTableWorker:"
PREPLICA Replica;
PCXTION Cxtion = NULL;
PWSTR CxtName = L"<null>";
PWSTR PartnerName = L"<null>";
PWSTR PartSrvName = L"<null>";
PCHAR CxtionState = "<null>";
BOOL PrintCxtion;
if (FlagOn(InfoTable->Info->Flags, NTFRSAPI_INFO_FLAGS_FULL)) {
return JET_errNoCurrentRecord;
}
//
// Table Descriptor
//
IPRINT2(InfoTable->Info, "\nTable Type: %ws for %ws\n",
TableDescriptor, InfoTable->Replica->ReplicaName->Name);
//
// Dump the change order record.
//
DbsDisplayRecordIPrint(TableCtx, InfoTable, TRUE, NULL, 0);
Replica = InfoTable->Replica;
//
// Find the cxtion for this CO
//
LOCK_CXTION_TABLE(Replica);
Cxtion = GTabLookupNoLock(Replica->Cxtions, &Coc->CxtionGuid, NULL);
PrintCxtion = (Cxtion != NULL) && (Cxtion->Inbound);
if (PrintCxtion) {
CxtionState = CxtionStateNames[Cxtion->State];
if (Cxtion->Name != NULL) {
if (Cxtion->Name->Name != NULL) {
CxtName = Cxtion->Name->Name;
}
}
if ((Cxtion->Partner != NULL) && (Cxtion->Partner->Name != NULL)) {
PartnerName = Cxtion->Partner->Name;
}
if (Cxtion->PartSrvName != NULL) {
PartSrvName = Cxtion->PartSrvName;
}
}
UNLOCK_CXTION_TABLE(Replica);
if (PrintCxtion) {
IPRINT3(InfoTable->Info, "Cxtion Name : %ws <- %ws\\%ws\n",
CxtName, PartnerName, PartSrvName);
IPRINT1(InfoTable->Info, "Cxtion State : %s\n", CxtionState);
}
return JET_errSuccess;
}
JET_ERR
InfoInLogTableWorker(
IN PTHREAD_CTX ThreadCtx,
IN PTABLE_CTX TableCtx,
IN PCHANGE_ORDER_COMMAND Coc,
IN PINFO_TABLE InfoTable
)
/*++
Routine Description:
This is a worker function passed to FrsEnumerateTable(). Each time
it is called it prints an entry into the info buffer.
Arguments:
ThreadCtx - Needed to access Jet.
TableCtx - A ptr to an IDTable context struct.
Coc - A ptr to a inbound log record (change order)
InfoTable
Thread Return Value:
A Jet error status. Success means call us with the next record.
Failure means don't call again and pass our status back to the
caller of FrsEnumerateTable().
--*/
{
return InfoInOutLogTableWorker(ThreadCtx, TableCtx, Coc, InfoTable,
L"Inbound Log Table");
}
JET_ERR
InfoOutLogTableWorker(
IN PTHREAD_CTX ThreadCtx,
IN PTABLE_CTX TableCtx,
IN PCHANGE_ORDER_COMMAND Coc,
IN PINFO_TABLE InfoTable
)
/*++
Routine Description:
This is a worker function passed to FrsEnumerateTable(). Each time
it is called it prints an entry into the info buffer.
Arguments:
ThreadCtx - Needed to access Jet.
TableCtx - A ptr to an IDTable context struct.
Coc - A ptr to a inbound log record (change order)
InfoTable
Thread Return Value:
A Jet error status. Success means call us with the next record.
Failure means don't call again and pass our status back to the
caller of FrsEnumerateTable().
--*/
{
return InfoInOutLogTableWorker(ThreadCtx, TableCtx, Coc, InfoTable,
L"Outbound Log Table");
}
DWORD
InfoPrintSingleTable(
IN PNTFRSAPI_INFO Info,
IN PWCHAR TableName,
IN TABLE_TYPE TableType,
IN PREPLICA Replica,
IN ULONG InfoIndexx,
IN PENUMERATE_TABLE_ROUTINE InfoTableWorker
)
/*++
Routine Description:
Display data for the specified table using the InfoPrint interface.
Arguments:
Info - ptr to the API Info ctx.
TableName - The name of the table
TableType - the Type code for the table.
Replica, -- ptr to the replica struct for the replica set.
InfoIndexx -- The ID of the index to use when enumerating the table.
InfoTableWorker -- The function to call to display each record.
Return Value:
jet error Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintSingleTable:"
JET_ERR jerr = JET_errSuccess;
PINFO_TABLE InfoTable = NULL;
InfoTable = FrsAlloc(sizeof(*InfoTable));
InfoTable->ThreadCtx = FrsAllocType(THREAD_CONTEXT_TYPE);
InfoTable->TableCtx = DbsCreateTableContext(TableType);
InfoTable->Info = Info;
InfoTable->Tabs = 0; /* Tabs + 1*/ // Pitch this tabs stuff.
IPRINT1(Info, "\n***** %ws\n", TableName);
//
// Setup a Jet Session (returning the session ID in ThreadCtx).
//
jerr = DbsCreateJetSession(InfoTable->ThreadCtx);
if (!JET_SUCCESS(jerr)) {
IPRINT2(Info,"ERROR - %ws: DbsCreateJetSession jet error %s.\n",
TableName, ErrLabelJet(jerr));
goto RETURN;
}
//
// Init the table context and open the ID table.
//
jerr = DbsOpenTable(InfoTable->ThreadCtx,
InfoTable->TableCtx,
ReplicaAddrToId(Replica),
TableType,
NULL);
if (!JET_SUCCESS(jerr)) {
IPRINT2(Info,"ERROR - %ws: DbsOpenTable jet error %s.\n",
TableName, ErrLabelJet(jerr));
goto RETURN;
}
//
// Replica
//
InfoTable->Replica = Replica;
//
// Scan thru the Table
//
jerr = FrsEnumerateTable(InfoTable->ThreadCtx,
InfoTable->TableCtx,
InfoIndexx,
InfoTableWorker,
InfoTable);
//
// We're done. Return success if we made it to the end
//
if (jerr != JET_errNoCurrentRecord &&
jerr != JET_wrnTableEmpty) {
IPRINT2(Info,"ERROR - %ws: FrsEnumerateTable jet error %s.\n",
TableName, ErrLabelJet(jerr));
}
RETURN:
//
// Done
//
InfoTable = InfoFreeInfoTable(InfoTable, Info);
return jerr;
}
DWORD
InfoPrintTables(
IN PNTFRSAPI_INFO Info,
IN PWCHAR TableDescriptor,
IN TABLE_TYPE TableType,
IN ULONG InfoIndexx,
IN PENUMERATE_TABLE_ROUTINE InfoTableWorker
)
/*++
Routine Description:
Return internal info on a DB Table (see private\net\inc\ntfrsapi.h).
Arguments:
Info - RPC output buffer
TableDescriptor - Text string for output
TableType - Table type code (from schema.h)
InfoIndexx - Table index to use for enumeration (from schema.h)
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintTables:"
PVOID Key;
PREPLICA Replica = NULL;
extern PGEN_TABLE ReplicasByGuid;
//
// Check for single instance tables.
//
if ((TableType == ConfigTablex) ||
(TableType == ServiceTablex)) {
InfoPrintSingleTable(Info,
TableDescriptor,
TableType,
Replica,
InfoIndexx,
InfoTableWorker);
return ERROR_SUCCESS;
}
//
// For the given table type, dump info for all replica sets.
//
IPRINT1(Info, "NTFRS %ws\n", TableDescriptor);
Key = NULL;
while (Replica = GTabNextDatum(ReplicasByGuid, &Key)) {
InfoPrintSingleTable(Info,
Replica->ReplicaName->Name,
TableType,
Replica,
InfoIndexx,
InfoTableWorker);
}
return ERROR_SUCCESS;
}
DWORD
InfoPrintMemory(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs
)
/*++
Routine Description:
Return internal info on memory usage (see private\net\inc\ntfrsapi.h).
Arguments:
Info - RPC output buffer
Tabs - number of tabs
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintMemory:"
FrsPrintAllocStats(0, Info, Tabs);
FrsPrintRpcStats(0, Info, Tabs);
return ERROR_SUCCESS;
}
DWORD
InfoPrintThreads(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs
)
/*++
Routine Description:
Return internal info on thread usage (see private\net\inc\ntfrsapi.h).
Arguments:
Info - RPC output buffer
Tabs - number of tabs
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintThreads:"
FrsPrintThreadStats(0, Info, Tabs);
return ERROR_SUCCESS;
}
VOID
FrsPrintStageStats(
IN ULONG Severity,
IN PNTFRSAPI_INFO Info, OPTIONAL
IN DWORD Tabs
);
DWORD
InfoPrintStage(
IN PNTFRSAPI_INFO Info,
IN DWORD Tabs
)
/*++
Routine Description:
Return internal info on thread usage (see private\net\inc\ntfrsapi.h).
Arguments:
Info - RPC output buffer
Tabs - number of tabs
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoPrintStage:"
FrsPrintStageStats(0, Info, Tabs);
return ERROR_SUCCESS;
}
DWORD
InfoVerify(
IN ULONG BlobSize,
IN OUT PBYTE Blob
)
/*++
Routine Description:
Verify the consistency of the blob.
Arguments:
BlobSize - total bytes of Blob
Blob - details desired info and provides buffer for info
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "InfoVerify:"
DWORD WStatus;
PBYTE EoB;
PBYTE EoI;
PBYTE BoL;
PBYTE BoF;
PNTFRSAPI_INFO Info = (PNTFRSAPI_INFO)Blob;
//
// Assume success
//
WStatus = ERROR_SUCCESS;
//
// Not a valid blob
//
if (BlobSize < NTFRSAPI_INFO_HEADER_SIZE) {
WStatus = FRS_ERR_INVALID_SERVICE_PARAMETER;
goto CLEANUP;
}
//
// BlobSize must include the entire Blob
//
if (BlobSize != Info->SizeInChars) {
WStatus = FRS_ERR_INVALID_SERVICE_PARAMETER;
goto CLEANUP;
}
//
// Return our info version
//
Info->NtFrsMajor = NTFRS_MAJOR;
Info->NtFrsMinor = NTFRS_MINOR;
SetFlag(Info->Flags, NTFRSAPI_INFO_FLAGS_VERSION);
//
// Bad major
//
if (Info->Major != Info->NtFrsMajor) {
WStatus = FRS_ERR_INVALID_SERVICE_PARAMETER;
goto CLEANUP;
}
//
// Bad minor
//
if (Info->Minor != Info->NtFrsMinor) {
WStatus = FRS_ERR_INVALID_SERVICE_PARAMETER;
goto CLEANUP;
}
//
// Not large enough to verify internal consistency
//
if (Info->SizeInChars < sizeof(NTFRSAPI_INFO)) {
WStatus = FRS_ERR_INVALID_SERVICE_PARAMETER;
goto CLEANUP;
}
//
// Buffer full; done
//
if (FlagOn(Info->Flags, NTFRSAPI_INFO_FLAGS_FULL)) {
goto CLEANUP;
}
//
// Verify internal offsets
//
// make this into a subroutine (table driven?)
//
EoB = Blob + BlobSize;
EoI = ((PBYTE)Info) + (Info->SizeInChars);
BoL = (PBYTE)(((PCHAR)Info) + Info->OffsetToLines);
BoF = (PBYTE)(((PCHAR)Info) + Info->OffsetToFree);
if (EoI > EoB ||
BoL > EoB ||
BoF > EoB ||
EoI < Blob ||
BoL < Blob ||
BoF < Blob) {
WStatus = FRS_ERR_INVALID_SERVICE_PARAMETER;
goto CLEANUP;
}
//
// No free space in buffer; done
//
if (BoF == EoB) {
SetFlag(Info->Flags, NTFRSAPI_INFO_FLAGS_FULL);
goto CLEANUP;
}
CLEANUP:
return WStatus;
}
DWORD
Info(
IN ULONG BlobSize,
IN OUT PBYTE Blob
)
/*++
Routine Description:
Return internal info (see private\net\inc\ntfrsapi.h).
Arguments:
BlobSize - total bytes of Blob
Blob - details desired info and provides buffer for info
Return Value:
Win32 Status
--*/
{
#undef DEBSUB
#define DEBSUB "Info:"
DWORD WStatus;
PBYTE EoB;
PBYTE EoI;
PBYTE BoL;
PBYTE BoF;
ULONG i;
PNTFRSAPI_INFO Info = (PNTFRSAPI_INFO)Blob;
ULONG ProductType;
ULONG Arch;
try {
//
// Verify the blob
//
WStatus = InfoVerify(BlobSize, Blob);
if (!WIN_SUCCESS(WStatus)) {
goto cleanup;
}
//
// Full buffer; done
//
if (FlagOn(Info->Flags, NTFRSAPI_INFO_FLAGS_FULL)) {
goto cleanup;
}
if (Info->TypeOfInfo == NTFRSAPI_INFO_TYPE_VERSION) {
IPRINT0(Info, "NtFrs Version Information\n");
IPRINT1(Info, " NtFrs Major : %d\n", NtFrsMajor);
IPRINT1(Info, " NtFrs Minor : %d\n", NtFrsMinor);
// IPRINT1(Info, " NtFrs Module : %s\n", NtFrsModule);
IPRINT2(Info, " NtFrs Compiled on : %s %s\n", NtFrsDate, NtFrsTime);
#if NTFRS_TEST
IPRINT0(Info, " NTFRS_TEST Enabled\n");
#else NTFRS_TEST
IPRINT0(Info, " NTFRS_TEST Disabled\n");
#endif NTFRS_TEST
i = 0;
while (LatestChanges[i] != NULL) {
IPRINT1(Info, " %s\n", LatestChanges[i]);
i++;
}
IPRINT4(Info, "OS Version %d.%d (%d) - %w\n",
OsInfo.dwMajorVersion, OsInfo.dwMinorVersion,
OsInfo.dwBuildNumber, OsInfo.szCSDVersion);
ProductType = (ULONG) OsInfo.wProductType;
IPRINT4(Info, "SP (%hd.%hd) SM: 0x%04hx PT: 0x%02x\n",
OsInfo.wServicePackMajor, OsInfo.wServicePackMinor,
OsInfo.wSuiteMask, ProductType);
Arch = SystemInfo.wProcessorArchitecture;
if (Arch >= ARRAY_SZ(ProcessorArchName)) {
Arch = ARRAY_SZ(ProcessorArchName)-1;
}
IPRINT5(Info, "Processor: %s Level: 0x%04hx Revision: 0x%04hx Processor num/mask: %d/%08x\n",
ProcessorArchName[Arch], SystemInfo.wProcessorLevel,
SystemInfo.wProcessorRevision, SystemInfo.dwNumberOfProcessors,
SystemInfo.dwActiveProcessorMask);
goto cleanup;
} else if (Info->TypeOfInfo == NTFRSAPI_INFO_TYPE_SETS) {
WStatus = InfoPrintDbSets(Info, 0);
} else if (Info->TypeOfInfo == NTFRSAPI_INFO_TYPE_DS) {
WStatus = InfoPrintDs(Info, 0);
} else if (Info->TypeOfInfo == NTFRSAPI_INFO_TYPE_MEMORY) {
WStatus = InfoPrintMemory(Info, 0);
} else if (Info->TypeOfInfo == NTFRSAPI_INFO_TYPE_IDTABLE) {
WStatus = InfoPrintTables(Info,
L"ID TABLES",
IDTablex,
GuidIndexx,
InfoIDTableWorker);
} else if (Info->TypeOfInfo == NTFRSAPI_INFO_TYPE_INLOG) {
WStatus = InfoPrintTables(Info,
L"INLOG TABLES",
INLOGTablex,
ILSequenceNumberIndexx,
InfoInLogTableWorker);
} else if (Info->TypeOfInfo == NTFRSAPI_INFO_TYPE_OUTLOG) {
WStatus = InfoPrintTables(Info,
L"OUTLOG TABLES",
OUTLOGTablex,
OLSequenceNumberIndexx,
InfoOutLogTableWorker);
} else if (Info->TypeOfInfo == NTFRSAPI_INFO_TYPE_CONFIGTABLE) {
WStatus = InfoPrintTables(Info,
L"CONFIG TABLE",
ConfigTablex,
ReplicaSetNameIndexx,
InfoConfigTableWorker);
} else if (Info->TypeOfInfo == NTFRSAPI_INFO_TYPE_THREADS) {
WStatus = InfoPrintThreads(Info, 0);
} else if (Info->TypeOfInfo == NTFRSAPI_INFO_TYPE_STAGE) {
WStatus = InfoPrintStage(Info, 0);
} else {
IPRINT1(Info, "NtFrs Doesn't understand TypeOfInfo %d\n",
Info->TypeOfInfo);
}
cleanup:;
} except (EXCEPTION_EXECUTE_HANDLER) {
GET_EXCEPTION_CODE(WStatus);
}
//
// Clean up any handles, events, memory, ...
//
try {
} except (EXCEPTION_EXECUTE_HANDLER) {
GET_EXCEPTION_CODE(WStatus);
}
return WStatus;
}