/*++ Copyright (c) 2000 Microsoft Corporation Module Name: fttest.c Abstract: Component test for Ds*ForestTrustInformation API Author: Cliff Van Dyke (CliffV) August 11, 2000 Environment: Revision History: --*/ #include #include #include #include // #include // #include #include #include // #include #include #include #include #include #include #include VOID NlpDumpSid( IN DWORD DebugFlag, IN PSID Sid OPTIONAL ) /*++ Routine Description: Dumps a SID to the debugger output Arguments: DebugFlag - Debug flag to pass on to NlPrintRoutine Sid - SID to output Return Value: none --*/ { // // Output the SID // if ( Sid == NULL ) { printf( "(null)\n"); } else { UNICODE_STRING SidString; NTSTATUS Status; Status = RtlConvertSidToUnicodeString( &SidString, Sid, TRUE ); if ( !NT_SUCCESS(Status) ) { printf( "Invalid 0x%lX\n", Status ); } else { printf( "%wZ\n", &SidString ); RtlFreeUnicodeString( &SidString ); } } UNREFERENCED_PARAMETER( DebugFlag ); } VOID PrintTime( LPSTR Comment, LARGE_INTEGER ConvertTime ) /*++ Routine Description: Print the specified time Arguments: Comment - Comment to print in front of the time Time - GMT time to print (Nothing is printed if this is zero) Return Value: None --*/ { // // If we've been asked to convert an NT GMT time to ascii, // Do so // if ( ConvertTime.QuadPart != 0 ) { LARGE_INTEGER LocalTime; TIME_FIELDS TimeFields; NTSTATUS Status; printf( "%s", Comment ); Status = RtlSystemTimeToLocalTime( &ConvertTime, &LocalTime ); if ( !NT_SUCCESS( Status )) { printf( "Can't convert time from GMT to Local time\n" ); LocalTime = ConvertTime; } RtlTimeToTimeFields( &LocalTime, &TimeFields ); printf( "%8.8lx %8.8lx = %ld/%ld/%ld %ld:%2.2ld:%2.2ld\n", ConvertTime.LowPart, ConvertTime.HighPart, TimeFields.Month, TimeFields.Day, TimeFields.Year, TimeFields.Hour, TimeFields.Minute, TimeFields.Second ); } } LPSTR FindSymbolicNameForStatus( DWORD Id ) { ULONG i; i = 0; if (Id == 0) { return "STATUS_SUCCESS"; } if (Id & 0xC0000000) { while (ntstatusSymbolicNames[ i ].SymbolicName) { if (ntstatusSymbolicNames[ i ].MessageId == (NTSTATUS)Id) { return ntstatusSymbolicNames[ i ].SymbolicName; } else { i += 1; } } } while (winerrorSymbolicNames[ i ].SymbolicName) { if (winerrorSymbolicNames[ i ].MessageId == Id) { return winerrorSymbolicNames[ i ].SymbolicName; } else { i += 1; } } #ifdef notdef while (neteventSymbolicNames[ i ].SymbolicName) { if (neteventSymbolicNames[ i ].MessageId == Id) { return neteventSymbolicNames[ i ].SymbolicName } else { i += 1; } } #endif // notdef return NULL; } VOID PrintStatus( NET_API_STATUS NetStatus ) /*++ Routine Description: Print a net status code. Arguments: NetStatus - The net status code to print. Return Value: None --*/ { printf( "Status = %lu 0x%lx", NetStatus, NetStatus ); switch (NetStatus) { case NERR_Success: printf( " NERR_Success" ); break; case NERR_DCNotFound: printf( " NERR_DCNotFound" ); break; case NERR_UserNotFound: printf( " NERR_UserNotFound" ); break; case NERR_NetNotStarted: printf( " NERR_NetNotStarted" ); break; case NERR_WkstaNotStarted: printf( " NERR_WkstaNotStarted" ); break; case NERR_ServerNotStarted: printf( " NERR_ServerNotStarted" ); break; case NERR_BrowserNotStarted: printf( " NERR_BrowserNotStarted" ); break; case NERR_ServiceNotInstalled: printf( " NERR_ServiceNotInstalled" ); break; case NERR_BadTransactConfig: printf( " NERR_BadTransactConfig" ); break; default: printf( " %s", FindSymbolicNameForStatus( NetStatus ) ); break; } printf( "\n" ); } VOID DumpFtinfo( PLSA_FOREST_TRUST_INFORMATION Ftinfo ) /*++ Routine Description: Dumps the buffer content on to the debugger output. Arguments: Buffer: buffer pointer. BufferSize: size of the buffer. Return Value: none --*/ { ULONG Index; if ( Ftinfo == NULL ) { printf( " (null)\n"); } else { for ( Index=0; IndexRecordCount; Index++ ) { switch ( Ftinfo->Entries[Index]->ForestTrustType ) { case ForestTrustTopLevelName: printf( " TLN: %wZ", &Ftinfo->Entries[Index]->ForestTrustData.TopLevelName ); break; case ForestTrustTopLevelNameEx: printf( " TEX: %wZ", &Ftinfo->Entries[Index]->ForestTrustData.TopLevelName ); break; case ForestTrustDomainInfo: printf( " Dom: %wZ (%wZ)", &Ftinfo->Entries[Index]->ForestTrustData.DomainInfo.DnsName, &Ftinfo->Entries[Index]->ForestTrustData.DomainInfo.NetbiosName ); break; default: printf( " Invalid Type: %ld", Ftinfo->Entries[Index]->ForestTrustType ); } if ( Ftinfo->Entries[Index]->Flags ) { ULONG Flags = Ftinfo->Entries[Index]->Flags; printf(" (" ); #define DoFlag( _flag, _text ) \ if ( Flags & _flag ) { \ printf( _text ); \ Flags &= ~_flag; \ } switch ( Ftinfo->Entries[Index]->ForestTrustType ) { case ForestTrustTopLevelName: case ForestTrustTopLevelNameEx: DoFlag( LSA_TLN_DISABLED_NEW, " TlnNew" ); DoFlag( LSA_TLN_DISABLED_ADMIN, " TlnAdmin" ); DoFlag( LSA_TLN_DISABLED_CONFLICT, " TlnConflict" ); } switch ( Ftinfo->Entries[Index]->ForestTrustType ) { case ForestTrustDomainInfo: DoFlag( LSA_SID_DISABLED_ADMIN, " SidAdmin" ); DoFlag( LSA_SID_DISABLED_CONFLICT, " SidConflict" ); DoFlag( LSA_NB_DISABLED_ADMIN, " NbAdmin" ); DoFlag( LSA_NB_DISABLED_CONFLICT, " NbConflict" ); } if ( Flags != 0 ) { printf(" 0x%lX", Flags); } printf(")" ); } switch ( Ftinfo->Entries[Index]->ForestTrustType ) { case ForestTrustDomainInfo: printf(" "); NlpDumpSid( 0, Ftinfo->Entries[Index]->ForestTrustData.DomainInfo.Sid ); break; default: printf("\n"); break; } } } } // // Structure describing an Ftinfo entry // typedef struct _AN_ENTRY { ULONG Flags; LSA_FOREST_TRUST_RECORD_TYPE ForestTrustType; // type of record #define TLN ForestTrustTopLevelName #define TLNEX ForestTrustTopLevelNameEx #define DOM ForestTrustDomainInfo #define EOD (DOM+1) LPWSTR Name; PSID Sid; LPWSTR NetbiosName; } AN_ENTRY, *PAN_ENTRY; // // Define template FTinfo structures. // AN_ENTRY Ftinfo0[] = { { 0, TLN, L"acme.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo1[] = { { 0, TLN, L"acme.com" }, { 0, TLN, L"ms.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo2[] = { { 0, TLN, L"acme.com" }, { 0, TLN, L"z.au" }, { 0, EOD }, }; AN_ENTRY Ftinfo3[] = { { 0, TLN, L"z.au" }, { 0, EOD }, }; AN_ENTRY Ftinfo4[] = { { 0, TLN, L"corp.acme.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo5[] = { { 0, TLN, L"x.corp.acme.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo6[] = { { 0, TLN, L"acme.com" }, { LSA_TLN_DISABLED_ADMIN, TLN, L"ms.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo7[] = { { 0, TLN, L"acme.com" }, { 0xFFFFFFFF, TLN, L"ms.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo8[] = { { 0, TLN, L"acme.com" }, { LSA_TLN_DISABLED_ADMIN, TLN, L"ms.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo9[] = { { 0, TLN, L"acme.com" }, { LSA_TLN_DISABLED_ADMIN, TLN, L"b.a.ms.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo10[] = { { 0, TLN, L"acme.com" }, { 0, TLN, L"a.ms.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo11[] = { { 0, TLN, L"acme.com" }, { 0, TLNEX, L"a.acme.com" }, { 0, EOD }, }; SID Sid1 = { 1, 1, SECURITY_NT_AUTHORITY, 1 }; SID Sid2 = { 1, 1, SECURITY_NT_AUTHORITY, 2 }; SID Sid3 = { 1, 1, SECURITY_NT_AUTHORITY, 3 }; SID Sid4 = { 1, 1, SECURITY_NT_AUTHORITY, 4 }; SID Sid5 = { 1, 1, SECURITY_NT_AUTHORITY, 5 }; AN_ENTRY Ftinfo12[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB" }, { 0, EOD }, }; AN_ENTRY Ftinfo13[] = { { 0, TLN, L"acme.com" }, { LSA_SID_DISABLED_ADMIN, DOM, L"corp.acme.com", &Sid1, L"CORP_NB" }, { 0, EOD }, }; AN_ENTRY Ftinfo13a[] = { { 0, TLN, L"acme.com" }, { 0xFFFFFFFF, DOM, L"corp.acme.com", &Sid1, L"CORP_NB" }, { 0, EOD }, }; AN_ENTRY Ftinfo14[] = { { 0, TLN, L"acme.com" }, { 0, TLN, L"acme.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo15[] = { { 0, TLN, L"acme.com" }, { 0, TLN, L"a.acme.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo16[] = { { 0, TLN, L"acme.com" }, { 0, TLN, L"acme.com" }, { 0, TLN, L"a.acme.com" }, { 0, TLN, L"b.acme.com" }, { 0, TLN, L"ms.com" }, { 0, EOD }, }; AN_ENTRY Ftinfo17[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid3, L"CORP_NB2" }, { 0, EOD }, }; AN_ENTRY Ftinfo18[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid1, L"CORP_NB2" }, { 0, EOD }, }; AN_ENTRY Ftinfo19[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid1, L"CORP_NB2" }, { 0, DOM, L"c3.corp.acme.com", &Sid1, L"CORP_NB3" }, { 0, DOM, L"c4.corp.acme.com", &Sid1, L"CORP_NB4" }, { 0, EOD }, }; AN_ENTRY Ftinfo20[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid3, L"CORP_NB2" }, { 0, DOM, L"c3.corp.acme.com", &Sid4, L"CORP_NB3" }, { 0, DOM, L"c4.corp.acme.com", &Sid5, L"CORP_NB4" }, { 0, EOD }, }; AN_ENTRY Ftinfo21[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { LSA_SID_DISABLED_ADMIN, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid3, L"CORP_NB2" }, { LSA_SID_DISABLED_ADMIN, DOM, L"c3.corp.acme.com", &Sid4, L"CORP_NB3" }, { 0, DOM, L"c4.corp.acme.com", &Sid5, L"CORP_NB4" }, { 0, EOD }, }; AN_ENTRY Ftinfo22[] = { { 0, TLN, L"acme.com" }, { LSA_SID_DISABLED_ADMIN, DOM, L"ms.com", &Sid1, L"CORP_NB" }, { 0, EOD }, }; AN_ENTRY Ftinfo23[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.ms.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.ms.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.ms.com", &Sid3, L"CORP_NB2" }, { 0, EOD }, }; AN_ENTRY Ftinfo24[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid3, L"CORP_NB2" }, { 0, DOM, L"c3.corp.acme.com", &Sid4, L"CORP_NB3" }, { 0, EOD }, }; AN_ENTRY Ftinfo24a[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid3, L"CORP_NB2" }, { 0, DOM, L"c3.corp.acme.com", &Sid4, L"CORP_NB3" }, { LSA_NB_DISABLED_ADMIN, DOM, L"c4.corp.acme.com", &Sid5, L"CORP_NB4" }, { 0, EOD }, }; AN_ENTRY Ftinfo24b[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid3, L"CORP_NB2" }, { 0, DOM, L"c3.corp.acme.com", &Sid4, L"CORP_NB3" }, { LSA_NB_DISABLED_CONFLICT, DOM, L"c4.corp.acme.com", &Sid5, L"CORP_NB4" }, { 0, EOD }, }; AN_ENTRY Ftinfo24c[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid3, L"CORP_NB3" }, { LSA_NB_DISABLED_ADMIN|LSA_NB_DISABLED_CONFLICT, DOM, L"c3.corp.acme.com", &Sid4, L"CORP_NB2" }, { 0, EOD }, }; AN_ENTRY Ftinfo24d[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid3, L"CORP_NB3" }, { 0xFFFFFFFF, DOM, L"c3.corp.acme.com", &Sid4, L"CORP_NB2" }, { 0, EOD }, }; AN_ENTRY Ftinfo24e[] = { { 0, TLN, L"acme.com" }, { 0, DOM, L"corp.acme.com", &Sid1, L"CORP_NB0" }, { 0, DOM, L"c1.corp.acme.com", &Sid2, L"CORP_NB1" }, { 0, DOM, L"c2.corp.acme.com", &Sid3, L"CORP_NB3" }, { 0, DOM, L"c3.corp.acme.com", &Sid4, L"CORP_NB2" }, { 0, EOD }, }; // // Structure describine test cases // typedef struct _TEST_CASE { PAN_ENTRY OldFtinfo; #define PREVIOUS ((PAN_ENTRY) 1) PAN_ENTRY NewFtinfo; LPSTR Description; } TEST_CASE, PTEST_CASE; // // Define the test cases // TEST_CASE TestCases[] = { { NULL, Ftinfo0, "Just acme.com TLN" }, { NULL, Ftinfo1, "acme.com and ms.com TLN" }, { NULL, Ftinfo2, "Same but switch the alphabetical order" }, { NULL, Ftinfo3, "Have no TLN for the forest (Should fail w/ ERROR_INVALID_PARAMETER)" }, { NULL, Ftinfo0, "Build acme.com again" }, { PREVIOUS, Ftinfo1, "Add a new ms.com TLN" }, { PREVIOUS, Ftinfo1, "Ensure the new bit doesn't go away" }, { NULL, Ftinfo4, "Exact match on corp.acme.com TLN" }, { NULL, Ftinfo5, "Only child of corp.acme.com TLN (Should fail w/ ERROR_INVALID_PARAMETER)" }, { Ftinfo6, Ftinfo1, "Ensure a disabled TLN stays disabled" }, { Ftinfo7, Ftinfo1, "Ensure all bits are preserved in a TLN" }, { Ftinfo8, Ftinfo10, "Ensure a disabled TLN stays disabled in a child" }, { Ftinfo9, Ftinfo10, "Ensure a disabled TLN does *not* disable a parent" }, { NULL, Ftinfo11, "Ensure a TLNEX is ignored in new" }, { Ftinfo11, Ftinfo0, "Ensure a TLNEX is copied from old" }, { NULL, Ftinfo12, "Trivial single domain forest" }, { Ftinfo13, Ftinfo12, "Ensure a disabled domain remains disabled" }, { NULL, Ftinfo14, "Drop duplicate new TLN entries" }, { NULL, Ftinfo15, "... even if the duplicate is subordinate" }, { NULL, Ftinfo16, "... even if there are many of them" }, { NULL, Ftinfo17, "Try multiple domain entries" }, { NULL, Ftinfo18, "Duplicate Sids are bad" }, { NULL, Ftinfo19, "... even if there are many of them" }, { Ftinfo21, Ftinfo20, "Ensure multiple disabled domains remain disabled" }, { Ftinfo13, Ftinfo0, "Don't let an old disabled domain entry go away" }, { Ftinfo22, Ftinfo0, "... even if there's no TLN for the domain entry" }, { Ftinfo17, Ftinfo20, "Add a new domain" }, { Ftinfo20, Ftinfo17, "Delete old domains" }, { NULL, Ftinfo23, "Ensure there's a TLN for every domain" }, { Ftinfo13a,Ftinfo12, "Ensure all of the possible flag bits are preserved" }, { Ftinfo24a,Ftinfo24, "Ensure that a netbios admin disabled bit doesn't disappear" }, { Ftinfo24b,Ftinfo24, "... but that a netbios conflict does" }, { Ftinfo24c,Ftinfo24, "... Get it right even if the NB entry moves to different sid" }, { Ftinfo24d,Ftinfo24, "... and that all of the other flag bits stay put" }, { PREVIOUS, Ftinfo24e,"... and that we self repait when the trusted domain stops lying" }, }; PLSA_FOREST_TRUST_INFORMATION BuildFtinfo( PAN_ENTRY AnEntry ) /*++ Routine Description: Builds a FtInfo array from the "easy to initialize" templates. Arguments: AnEntry - Pointer to the first entry. Return Value: Returns a real ftinfo array. If this weren't a cheesy test program, the caller should free this memory. --*/ { PAN_ENTRY CurrentEntry; ULONG CurrentIndex; PLSA_FOREST_TRUST_INFORMATION Ftinfo; // // NULL is OK // if ( AnEntry == NULL ) { return NULL; } // // Allocate the return array // Ftinfo = LocalAlloc( 0, sizeof(*Ftinfo) ); if ( Ftinfo == NULL ) { printf( "No memory\n"); return NULL; } // // Count the number of entries // Ftinfo->RecordCount = 0; for ( CurrentEntry=AnEntry; CurrentEntry->ForestTrustType != EOD; CurrentEntry++ ) { Ftinfo->RecordCount ++; } // // Allocate the array of entry pointers. // Ftinfo->Entries = LocalAlloc( 0, sizeof(PLSA_FOREST_TRUST_RECORD) * Ftinfo->RecordCount ); if ( Ftinfo->Entries == NULL ) { printf( "No memory\n"); return NULL; } // // Loop through the entries. // CurrentIndex = 0; for ( CurrentEntry=AnEntry; CurrentEntry->ForestTrustType != EOD; CurrentEntry++ ) { // // Allocate the entry // Ftinfo->Entries[CurrentIndex] = LocalAlloc( LMEM_ZEROINIT, sizeof(LSA_FOREST_TRUST_RECORD) ); if ( Ftinfo->Entries[CurrentIndex] == NULL ) { printf( "No memory\n"); return NULL; } // // Fill it in // Ftinfo->Entries[CurrentIndex]->ForestTrustType = CurrentEntry->ForestTrustType; Ftinfo->Entries[CurrentIndex]->Flags = CurrentEntry->Flags; switch ( CurrentEntry->ForestTrustType ) { case TLN: case TLNEX: RtlInitUnicodeString( &Ftinfo->Entries[CurrentIndex]->ForestTrustData.TopLevelName, CurrentEntry->Name ); break; case DOM: RtlInitUnicodeString( &Ftinfo->Entries[CurrentIndex]->ForestTrustData.DomainInfo.DnsName, CurrentEntry->Name ); Ftinfo->Entries[CurrentIndex]->ForestTrustData.DomainInfo.Sid = CurrentEntry->Sid; RtlInitUnicodeString( &Ftinfo->Entries[CurrentIndex]->ForestTrustData.DomainInfo.NetbiosName, CurrentEntry->NetbiosName ); break; default: printf( "Bad forest trust type\n"); return NULL; } CurrentIndex ++; } return Ftinfo; } int __cdecl main ( IN int argc, IN char ** argv ) { NET_API_STATUS NetStatus; PLSA_FOREST_TRUST_INFORMATION OldFtinfo; PLSA_FOREST_TRUST_INFORMATION NewFtinfo; PLSA_FOREST_TRUST_INFORMATION OutputFtinfo = NULL; ULONG CaseIndex; ULONG FirstIndex = 0; // // If an argument is specified, // it is the test number to start with. // if ( argc > 1 ) { char *end; FirstIndex = strtoul( argv[1], &end, 10 ); } // // Loop through the list of tests // for ( CaseIndex=FirstIndex; CaseIndex<(sizeof(TestCases)/sizeof(TestCases[0])); CaseIndex++ ) { printf( "\nCase %ld: %s\n", CaseIndex, TestCases[CaseIndex].Description ); // // Build the test case FTINFO structures // if ( TestCases[CaseIndex].OldFtinfo == PREVIOUS ) { OldFtinfo = OutputFtinfo; } else { OldFtinfo = BuildFtinfo( TestCases[CaseIndex].OldFtinfo ); } NewFtinfo = BuildFtinfo( TestCases[CaseIndex].NewFtinfo ); // // Display them // printf(" Old Ftinfo:\n"); DumpFtinfo( OldFtinfo ); printf(" New Ftinfo:\n"); DumpFtinfo( NewFtinfo ); // // Merge them // NetStatus = DsMergeForestTrustInformationW( L"corp.acme.com", NewFtinfo, OldFtinfo, &OutputFtinfo ); if ( NetStatus != NERR_Success ) { printf( "DsMergeForestTrustInformationW failed: "); PrintStatus( NetStatus ); } else { printf(" Result Ftinfo:\n"); DumpFtinfo( OutputFtinfo ); } } printf("\n\nYee haw. We're done.\n"); return 0; }