mirror of https://github.com/tongzx/nt5src
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.
2918 lines
78 KiB
2918 lines
78 KiB
/*++
|
|
|
|
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;
|
|
}
|