LCOV - code coverage report
Current view: top level - source4/torture/rpc - drsuapi.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 215 383 56.1 %
Date: 2021-09-23 10:06:22 Functions: 13 15 86.7 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    DRSUapi tests
       5             : 
       6             :    Copyright (C) Andrew Tridgell 2003
       7             :    Copyright (C) Stefan (metze) Metzmacher 2004
       8             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
       9             : 
      10             :    This program is free software; you can redistribute it and/or modify
      11             :    it under the terms of the GNU General Public License as published by
      12             :    the Free Software Foundation; either version 3 of the License, or
      13             :    (at your option) any later version.
      14             :    
      15             :    This program is distributed in the hope that it will be useful,
      16             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :    GNU General Public License for more details.
      19             :    
      20             :    You should have received a copy of the GNU General Public License
      21             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      22             : */
      23             : 
      24             : #include "includes.h"
      25             : #include "librpc/gen_ndr/ndr_drsuapi_c.h"
      26             : #include "torture/rpc/torture_rpc.h"
      27             : #include "param/param.h"
      28             : 
      29             : #define TEST_MACHINE_NAME "torturetest"
      30             : 
      31           4 : bool test_DsBind(struct dcerpc_pipe *p,
      32             :                  struct torture_context *tctx,
      33             :                  struct DsPrivate *priv)
      34             : {
      35             :         NTSTATUS status;
      36             :         struct drsuapi_DsBind r;
      37             :         struct drsuapi_DsBindInfo28 *bind_info28;
      38             :         struct drsuapi_DsBindInfoCtr bind_info_ctr;
      39             : 
      40           4 :         ZERO_STRUCT(bind_info_ctr);
      41           4 :         bind_info_ctr.length = 28;
      42             : 
      43           4 :         bind_info28 = &bind_info_ctr.info.info28;
      44           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_BASE;
      45           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
      46           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
      47           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
      48           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
      49           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
      50           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
      51           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
      52           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
      53           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
      54           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
      55           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
      56           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
      57           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
      58           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
      59           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
      60           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
      61           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
      62           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
      63           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
      64           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
      65           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
      66           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
      67           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
      68           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
      69           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
      70           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
      71           4 :         bind_info28->supported_extensions    |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
      72             : 
      73           4 :         GUID_from_string(DRSUAPI_DS_BIND_GUID, &priv->bind_guid);
      74             : 
      75           4 :         r.in.bind_guid = &priv->bind_guid;
      76           4 :         r.in.bind_info = &bind_info_ctr;
      77           4 :         r.out.bind_handle = &priv->bind_handle;
      78             : 
      79           4 :         torture_comment(tctx, "Testing DsBind\n");
      80             : 
      81           4 :         status = dcerpc_drsuapi_DsBind_r(p->binding_handle, tctx, &r);
      82           6 :         torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsBind");
      83             : 
      84             :         /* cache server supported extensions, i.e. bind_info */
      85           2 :         priv->srv_bind_info = r.out.bind_info->info.info28;
      86             : 
      87           2 :         return true;
      88             : }
      89             : 
      90           1 : static bool test_DsGetDomainControllerInfo(struct torture_context *tctx,
      91             :                                            struct DsPrivate *priv)
      92             : {
      93             :         NTSTATUS status;
      94           1 :         struct dcerpc_pipe *p = priv->drs_pipe;
      95             :         struct drsuapi_DsGetDomainControllerInfo r;
      96             :         union drsuapi_DsGetDCInfoCtr ctr;
      97             :         union drsuapi_DsGetDCInfoRequest req;
      98           1 :         int32_t level_out = 0;
      99           1 :         bool found = false;
     100             :         int i, j, k;
     101             :         
     102             :         struct {
     103             :                 const char *name;
     104             :                 WERROR expected;
     105           2 :         } names[] = { 
     106             :                 {       
     107           1 :                         .name = torture_join_dom_netbios_name(priv->join),
     108             :                         .expected = WERR_OK
     109             :                 },
     110             :                 {
     111           1 :                         .name = torture_join_dom_dns_name(priv->join),
     112             :                         .expected = WERR_OK
     113             :                 },
     114             :                 {
     115             :                         .name = "__UNKNOWN_DOMAIN__",
     116             :                         .expected = WERR_DS_OBJ_NOT_FOUND
     117             :                 },
     118             :                 {
     119             :                         .name = "unknown.domain.samba.example.com",
     120             :                         .expected = WERR_DS_OBJ_NOT_FOUND
     121             :                 },
     122             :         };
     123           1 :         int levels[] = {1, 2};
     124             :         int level;
     125             : 
     126           1 :         for (i=0; i < ARRAY_SIZE(levels); i++) {
     127           3 :                 for (j=0; j < ARRAY_SIZE(names); j++) {
     128           3 :                         level = levels[i];
     129           3 :                         r.in.bind_handle = &priv->bind_handle;
     130           3 :                         r.in.level = 1;
     131           3 :                         r.in.req = &req;
     132             :                         
     133           3 :                         r.in.req->req1.domain_name = names[j].name;
     134           3 :                         r.in.req->req1.level = level;
     135             : 
     136           3 :                         r.out.ctr = &ctr;
     137           3 :                         r.out.level_out = &level_out;
     138             :                         
     139           6 :                         torture_comment(tctx,
     140             :                                    "Testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
     141           6 :                                r.in.req->req1.level, r.in.req->req1.domain_name);
     142             :                 
     143           3 :                         status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(p->binding_handle, tctx, &r);
     144           3 :                         torture_assert_ntstatus_ok(tctx, status,
     145             :                                    "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
     146           3 :                         torture_assert_werr_equal(tctx,
     147             :                                                                           r.out.result, names[j].expected, 
     148             :                                            "DsGetDomainControllerInfo level with dns domain failed");
     149             :                 
     150           2 :                         if (!W_ERROR_IS_OK(r.out.result)) {
     151             :                                 /* If this was an error, we can't read the result structure */
     152           0 :                                 continue;
     153             :                         }
     154             : 
     155           2 :                         torture_assert_int_equal(tctx,
     156             :                                                  r.in.req->req1.level, *r.out.level_out,
     157             :                                                  "dcerpc_drsuapi_DsGetDomainControllerInfo in/out level differs");
     158             : 
     159           2 :                         switch (level) {
     160           2 :                         case 1:
     161           4 :                                 for (k=0; k < r.out.ctr->ctr1.count; k++) {
     162           4 :                                         if (strcasecmp_m(r.out.ctr->ctr1.array[k].netbios_name,
     163             :                                                          torture_join_netbios_name(priv->join)) == 0) {
     164           2 :                                                 found = true;
     165           2 :                                                 break;
     166             :                                         }
     167             :                                 }
     168           2 :                                 break;
     169           0 :                         case 2:
     170           0 :                                 for (k=0; k < r.out.ctr->ctr2.count; k++) {
     171           0 :                                         if (strcasecmp_m(r.out.ctr->ctr2.array[k].netbios_name,
     172             :                                                          torture_join_netbios_name(priv->join)) == 0) {
     173           0 :                                                 found = true;
     174           0 :                                                 priv->dcinfo = r.out.ctr->ctr2.array[k];
     175           0 :                                                 break;
     176             :                                         }
     177             :                                 }
     178           0 :                                 break;
     179             :                         }
     180           2 :                         torture_assert(tctx, found,
     181             :                                  "dcerpc_drsuapi_DsGetDomainControllerInfo: Failed to find the domain controller we just created during the join");
     182             :                 }
     183             :         }
     184             : 
     185           0 :         r.in.bind_handle = &priv->bind_handle;
     186           0 :         r.in.level = 1;
     187             : 
     188           0 :         r.out.ctr = &ctr;
     189           0 :         r.out.level_out = &level_out;
     190             : 
     191           0 :         r.in.req->req1.domain_name = "__UNKNOWN_DOMAIN__"; /* This is clearly ignored for this level */
     192           0 :         r.in.req->req1.level = -1;
     193             :         
     194           0 :         torture_comment(tctx, "Testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
     195           0 :                         r.in.req->req1.level, r.in.req->req1.domain_name);
     196             :         
     197           0 :         status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(p->binding_handle, tctx, &r);
     198             : 
     199           0 :         torture_assert_ntstatus_ok(tctx, status,
     200             :                                    "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
     201           0 :         torture_assert_werr_ok(tctx, r.out.result,
     202             :                                 "DsGetDomainControllerInfo with dns domain failed");
     203             :         
     204             :         {
     205           0 :                 const char *dc_account = talloc_asprintf(tctx, "%s\\%s$",
     206             :                                                          torture_join_dom_netbios_name(priv->join), 
     207             :                                                          priv->dcinfo.netbios_name);
     208           0 :                 torture_comment(tctx, "%s: Enum active LDAP sessions searching for %s\n", __func__, dc_account);
     209           0 :                 for (k=0; k < r.out.ctr->ctr01.count; k++) {
     210           0 :                         if (strcasecmp_m(r.out.ctr->ctr01.array[k].client_account,
     211             :                                          dc_account)) {
     212           0 :                                 found = true;
     213           0 :                                 break;
     214             :                         }
     215             :                 }
     216           0 :                 torture_assert(tctx, found,
     217             :                         "dcerpc_drsuapi_DsGetDomainControllerInfo level: Failed to find the domain controller in last logon records");
     218             :         }
     219             : 
     220             : 
     221           0 :         return true;
     222             : }
     223             : 
     224           1 : static bool test_DsWriteAccountSpn(struct torture_context *tctx,
     225             :                                    struct DsPrivate *priv)
     226             : {
     227             :         NTSTATUS status;
     228           1 :         struct dcerpc_pipe *p = priv->drs_pipe;
     229             :         struct drsuapi_DsWriteAccountSpn r;
     230             :         union drsuapi_DsWriteAccountSpnRequest req;
     231             :         struct drsuapi_DsNameString names[2];
     232             :         union drsuapi_DsWriteAccountSpnResult res;
     233             :         uint32_t level_out;
     234             : 
     235           1 :         r.in.bind_handle                = &priv->bind_handle;
     236           1 :         r.in.level                      = 1;
     237           1 :         r.in.req                        = &req;
     238             : 
     239           1 :         torture_comment(tctx, "Testing DsWriteAccountSpn\n");
     240             : 
     241           1 :         r.in.req->req1.operation     = DRSUAPI_DS_SPN_OPERATION_ADD;
     242           1 :         r.in.req->req1.unknown1      = 0;
     243           1 :         r.in.req->req1.object_dn     = priv->dcinfo.computer_dn;
     244           1 :         r.in.req->req1.count         = 2;
     245           1 :         r.in.req->req1.spn_names     = names;
     246           1 :         names[0].str = talloc_asprintf(tctx, "smbtortureSPN/%s",priv->dcinfo.netbios_name);
     247           1 :         names[1].str = talloc_asprintf(tctx, "smbtortureSPN/%s",priv->dcinfo.dns_name);
     248             : 
     249           1 :         r.out.res                       = &res;
     250           1 :         r.out.level_out                 = &level_out;
     251             : 
     252           1 :         status = dcerpc_drsuapi_DsWriteAccountSpn_r(p->binding_handle, tctx, &r);
     253           1 :         torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsWriteAccountSpn");
     254             : 
     255           1 :         r.in.req->req1.operation     = DRSUAPI_DS_SPN_OPERATION_DELETE;
     256           1 :         r.in.req->req1.unknown1              = 0;
     257             : 
     258           1 :         status = dcerpc_drsuapi_DsWriteAccountSpn_r(p->binding_handle, tctx, &r);
     259           1 :         torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsWriteAccountSpn");
     260             : 
     261           1 :         return true;
     262             : }
     263             : 
     264           1 : static bool test_DsReplicaGetInfo(struct torture_context *tctx,
     265             :                                   struct DsPrivate *priv)
     266             : {
     267             :         NTSTATUS status;
     268           1 :         struct dcerpc_pipe *p = priv->drs_pipe;
     269             :         struct drsuapi_DsReplicaGetInfo r;
     270             :         union drsuapi_DsReplicaGetInfoRequest req;
     271             :         union drsuapi_DsReplicaInfo info;
     272             :         enum drsuapi_DsReplicaInfoType info_type;
     273             :         int i;
     274             :         struct {
     275             :                 int32_t level;
     276             :                 int32_t infotype;
     277             :                 const char *obj_dn;
     278           1 :         } array[] = {
     279             :                 {       
     280             :                         DRSUAPI_DS_REPLICA_GET_INFO,
     281             :                         DRSUAPI_DS_REPLICA_INFO_NEIGHBORS,
     282             :                         NULL
     283             :                 },{
     284             :                         DRSUAPI_DS_REPLICA_GET_INFO,
     285             :                         DRSUAPI_DS_REPLICA_INFO_CURSORS,
     286             :                         NULL
     287             :                 },{
     288             :                         DRSUAPI_DS_REPLICA_GET_INFO,
     289             :                         DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA,
     290             :                         NULL
     291             :                 },{
     292             :                         DRSUAPI_DS_REPLICA_GET_INFO,
     293             :                         DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES,
     294             :                         NULL
     295             :                 },{
     296             :                         DRSUAPI_DS_REPLICA_GET_INFO,
     297             :                         DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES,
     298             :                         NULL
     299             :                 },{
     300             :                         DRSUAPI_DS_REPLICA_GET_INFO,
     301             :                         DRSUAPI_DS_REPLICA_INFO_PENDING_OPS,
     302             :                         NULL
     303             :                 },{
     304             :                         DRSUAPI_DS_REPLICA_GET_INFO2,
     305             :                         DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA,
     306             :                         NULL
     307             :                 },{
     308             :                         DRSUAPI_DS_REPLICA_GET_INFO2,
     309             :                         DRSUAPI_DS_REPLICA_INFO_CURSORS2,
     310             :                         NULL
     311             :                 },{
     312             :                         DRSUAPI_DS_REPLICA_GET_INFO2,
     313             :                         DRSUAPI_DS_REPLICA_INFO_CURSORS3,
     314             :                         NULL
     315             :                 },{
     316             :                         DRSUAPI_DS_REPLICA_GET_INFO2,
     317             :                         DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2,
     318             :                         NULL
     319             :                 },{
     320             :                         DRSUAPI_DS_REPLICA_GET_INFO2,
     321             :                         DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2,
     322             :                         NULL
     323             :                 },{
     324             :                         DRSUAPI_DS_REPLICA_GET_INFO2,
     325             :                         DRSUAPI_DS_REPLICA_INFO_REPSTO,
     326             :                         NULL
     327             :                 },{
     328             :                         DRSUAPI_DS_REPLICA_GET_INFO2,
     329             :                         DRSUAPI_DS_REPLICA_INFO_CLIENT_CONTEXTS,
     330             :                         "__IGNORED__"
     331             :                 },{
     332             :                         DRSUAPI_DS_REPLICA_GET_INFO2,
     333             :                         DRSUAPI_DS_REPLICA_INFO_UPTODATE_VECTOR_V1,
     334             :                         NULL
     335             :                 },{
     336             :                         DRSUAPI_DS_REPLICA_GET_INFO2,
     337             :                         DRSUAPI_DS_REPLICA_INFO_SERVER_OUTGOING_CALLS,
     338             :                         NULL
     339             :                 }
     340             :         };
     341             : 
     342           1 :         if (torture_setting_bool(tctx, "samba4", false)) {
     343           1 :                 torture_comment(tctx, "skipping DsReplicaGetInfo test against Samba4\n");
     344           1 :                 return true;
     345             :         }
     346             : 
     347           0 :         r.in.bind_handle        = &priv->bind_handle;
     348           0 :         r.in.req                = &req;
     349             : 
     350           0 :         for (i=0; i < ARRAY_SIZE(array); i++) {
     351             :                 const char *object_dn;
     352             : 
     353           0 :                 torture_comment(tctx, "Testing DsReplicaGetInfo level %d infotype %d\n",
     354             :                                 array[i].level, array[i].infotype);
     355             : 
     356           0 :                 object_dn = (array[i].obj_dn ? array[i].obj_dn : priv->domain_obj_dn);
     357             : 
     358           0 :                 r.in.level = array[i].level;
     359           0 :                 switch(r.in.level) {
     360           0 :                 case DRSUAPI_DS_REPLICA_GET_INFO:
     361           0 :                         r.in.req->req1.info_type     = array[i].infotype;
     362           0 :                         r.in.req->req1.object_dn     = object_dn;
     363           0 :                         ZERO_STRUCT(r.in.req->req1.source_dsa_guid);
     364           0 :                         break;
     365           0 :                 case DRSUAPI_DS_REPLICA_GET_INFO2:
     366           0 :                         r.in.req->req2.info_type     = array[i].infotype;
     367           0 :                         r.in.req->req2.object_dn     = object_dn;
     368           0 :                         ZERO_STRUCT(r.in.req->req2.source_dsa_guid);
     369           0 :                         r.in.req->req2.flags         = 0;
     370           0 :                         r.in.req->req2.attribute_name        = NULL;
     371           0 :                         r.in.req->req2.value_dn_str  = NULL;
     372           0 :                         r.in.req->req2.enumeration_context = 0;
     373           0 :                         break;
     374             :                 }
     375             : 
     376           0 :                 r.out.info              = &info;
     377           0 :                 r.out.info_type         = &info_type;
     378             : 
     379           0 :                 status = dcerpc_drsuapi_DsReplicaGetInfo_r(p->binding_handle, tctx, &r);
     380           0 :                 torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaGetInfo");
     381           0 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) {
     382           0 :                         torture_comment(tctx,
     383             :                                         "DsReplicaGetInfo level %d and/or infotype %d not supported by server\n",
     384             :                                         array[i].level, array[i].infotype);
     385             :                 } else {
     386           0 :                         torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaGetInfo");
     387             :                 }
     388             :         }
     389             : 
     390           0 :         return true;
     391             : }
     392             : 
     393           1 : static bool test_DsReplicaSync(struct torture_context *tctx,
     394             :                                 struct DsPrivate *priv)
     395             : {
     396             :         NTSTATUS status;
     397           1 :         struct dcerpc_pipe *p = priv->drs_pipe;
     398             :         int i;
     399             :         struct drsuapi_DsReplicaSync r;
     400             :         union drsuapi_DsReplicaSyncRequest sync_req;
     401             :         struct drsuapi_DsReplicaObjectIdentifier nc;
     402             :         struct dom_sid null_sid;
     403             :         struct {
     404             :                 int32_t level;
     405           1 :         } array[] = {
     406             :                 {       
     407             :                         1
     408             :                 }
     409             :         };
     410             : 
     411           1 :         if (!torture_setting_bool(tctx, "dangerous", false)) {
     412           1 :                 torture_comment(tctx, "DsReplicaSync disabled - enable dangerous tests to use\n");
     413           1 :                 return true;
     414             :         }
     415             : 
     416           0 :         if (torture_setting_bool(tctx, "samba4", false)) {
     417           0 :                 torture_comment(tctx, "skipping DsReplicaSync test against Samba4\n");
     418           0 :                 return true;
     419             :         }
     420             : 
     421           0 :         ZERO_STRUCT(null_sid);
     422             : 
     423           0 :         r.in.bind_handle        = &priv->bind_handle;
     424             : 
     425           0 :         for (i=0; i < ARRAY_SIZE(array); i++) {
     426           0 :                 torture_comment(tctx, "Testing DsReplicaSync level %d\n",
     427             :                                 array[i].level);
     428             : 
     429           0 :                 r.in.level = array[i].level;
     430           0 :                 switch(r.in.level) {
     431           0 :                 case 1:
     432           0 :                         nc.guid                                 = GUID_zero();
     433           0 :                         nc.sid                                  = null_sid;
     434           0 :                         nc.dn                                   = priv->domain_obj_dn?priv->domain_obj_dn:"";
     435             : 
     436           0 :                         sync_req.req1.naming_context            = &nc;
     437           0 :                         sync_req.req1.source_dsa_guid           = priv->dcinfo.ntds_guid;
     438           0 :                         sync_req.req1.source_dsa_dns            = NULL;
     439           0 :                         sync_req.req1.options                   = 16;
     440             : 
     441           0 :                         r.in.req                                = &sync_req;
     442           0 :                         break;
     443             :                 }
     444             : 
     445           0 :                 status = dcerpc_drsuapi_DsReplicaSync_r(p->binding_handle, tctx, &r);
     446           0 :                 torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaSync");
     447             :         }
     448             : 
     449           0 :         return true;
     450             : }
     451             : 
     452           1 : static bool test_DsReplicaUpdateRefs(struct torture_context *tctx,
     453             :                                      struct DsPrivate *priv)
     454             : {
     455             :         NTSTATUS status;
     456           1 :         struct dcerpc_pipe *p = priv->drs_pipe;
     457             :         struct drsuapi_DsReplicaUpdateRefs r;
     458             :         struct drsuapi_DsReplicaObjectIdentifier nc;
     459             :         struct GUID dest_dsa_guid;
     460             :         const char *dest_dsa_guid_str;
     461             :         struct dom_sid null_sid;
     462             : 
     463           1 :         ZERO_STRUCT(null_sid);
     464           1 :         dest_dsa_guid = GUID_random();
     465           1 :         dest_dsa_guid_str = GUID_string(tctx, &dest_dsa_guid);
     466             : 
     467           1 :         r.in.bind_handle = &priv->bind_handle;
     468           1 :         r.in.level       = 1; /* Only version 1 is defined presently */
     469             : 
     470             :         /* setup NC */
     471           1 :         nc.guid         = priv->domain_obj_dn ? GUID_zero():priv->domain_guid;
     472           1 :         nc.sid          = null_sid;
     473           1 :         nc.dn           = priv->domain_obj_dn ? priv->domain_obj_dn : "";
     474             : 
     475             :         /* default setup for request */
     476           1 :         r.in.req.req1.naming_context    = &nc;
     477           1 :         r.in.req.req1.dest_dsa_dns_name = talloc_asprintf(tctx, "%s._msdn.%s",
     478             :                                                                 dest_dsa_guid_str,
     479             :                                                                 priv->domain_dns_name);
     480           1 :         r.in.req.req1.dest_dsa_guid     = dest_dsa_guid;
     481             : 
     482             :         /* 1. deleting replica dest should fail */
     483           1 :         torture_comment(tctx, "delete: %s\n", r.in.req.req1.dest_dsa_dns_name);
     484           1 :         r.in.req.req1.options           = DRSUAPI_DRS_DEL_REF;
     485           1 :         status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
     486           1 :         torture_drsuapi_assert_call_werr(tctx, p,
     487             :                                          status, WERR_DS_DRA_REF_NOT_FOUND, &r,
     488             :                                          "dcerpc_drsuapi_DsReplicaUpdateRefs");
     489             : 
     490             :         /* 2. hopefully adding random replica dest should succeed */
     491           1 :         torture_comment(tctx, "add    : %s\n", r.in.req.req1.dest_dsa_dns_name);
     492           1 :         r.in.req.req1.options           = DRSUAPI_DRS_ADD_REF;
     493           1 :         status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
     494           1 :         torture_drsuapi_assert_call_werr(tctx, p,
     495             :                                          status, WERR_OK, &r,
     496             :                                          "dcerpc_drsuapi_DsReplicaUpdateRefs");
     497             : 
     498             :         /* 3. try adding same replica dest - should fail */
     499           1 :         torture_comment(tctx, "add    : %s\n", r.in.req.req1.dest_dsa_dns_name);
     500           1 :         r.in.req.req1.options           = DRSUAPI_DRS_ADD_REF;
     501           1 :         status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
     502           1 :         torture_drsuapi_assert_call_werr(tctx, p,
     503             :                                          status, WERR_DS_DRA_REF_ALREADY_EXISTS, &r,
     504             :                                          "dcerpc_drsuapi_DsReplicaUpdateRefs");
     505             : 
     506             :         /* 4. try resetting same replica dest - should succeed */
     507           1 :         torture_comment(tctx, "reset : %s\n", r.in.req.req1.dest_dsa_dns_name);
     508           1 :         r.in.req.req1.options           = DRSUAPI_DRS_DEL_REF | DRSUAPI_DRS_ADD_REF;
     509           1 :         status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
     510           1 :         torture_drsuapi_assert_call_werr(tctx, p,
     511             :                                          status, WERR_OK, &r,
     512             :                                          "dcerpc_drsuapi_DsReplicaUpdateRefs");
     513             : 
     514             :         /* 5. delete random replicate added at step 2. */
     515           1 :         torture_comment(tctx, "delete : %s\n", r.in.req.req1.dest_dsa_dns_name);
     516           1 :         r.in.req.req1.options           = DRSUAPI_DRS_DEL_REF;
     517           1 :         status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
     518           1 :         torture_drsuapi_assert_call_werr(tctx, p,
     519             :                                          status, WERR_OK, &r,
     520             :                                          "dcerpc_drsuapi_DsReplicaUpdateRefs");
     521             : 
     522             :         /* 6. try replace on non-existing replica dest - should succeed */
     523           1 :         torture_comment(tctx, "replace: %s\n", r.in.req.req1.dest_dsa_dns_name);
     524           1 :         r.in.req.req1.options           = DRSUAPI_DRS_DEL_REF | DRSUAPI_DRS_ADD_REF;
     525           1 :         status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
     526           1 :         torture_drsuapi_assert_call_werr(tctx, p,
     527             :                                          status, WERR_OK, &r,
     528             :                                          "dcerpc_drsuapi_DsReplicaUpdateRefs");
     529             : 
     530             :         /* 7. delete random replicate added at step 6. */
     531           1 :         torture_comment(tctx, "delete : %s\n", r.in.req.req1.dest_dsa_dns_name);
     532           1 :         r.in.req.req1.options           = DRSUAPI_DRS_DEL_REF;
     533           1 :         status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
     534           1 :         torture_drsuapi_assert_call_werr(tctx, p,
     535             :                                          status, WERR_OK, &r,
     536             :                                          "dcerpc_drsuapi_DsReplicaUpdateRefs");
     537             : 
     538           1 :         return true;
     539             : }
     540             : 
     541           1 : static bool test_DsGetNCChanges(struct torture_context *tctx,
     542             :                                 struct DsPrivate *priv)
     543             : {
     544             :         NTSTATUS status;
     545           1 :         struct dcerpc_pipe *p = priv->drs_pipe;
     546             :         int i;
     547             :         struct drsuapi_DsGetNCChanges r;
     548             :         union drsuapi_DsGetNCChangesRequest req;
     549             :         union drsuapi_DsGetNCChangesCtr ctr;
     550             :         struct drsuapi_DsReplicaObjectIdentifier nc;
     551             :         struct dom_sid null_sid;
     552             :         uint32_t level_out;
     553             :         struct {
     554             :                 uint32_t level;
     555           1 :         } array[] = {
     556             :                 {       
     557             :                         5
     558             :                 },
     559             :                 {       
     560             :                         8
     561             :                 }
     562             :         };
     563             : 
     564           1 :         if (torture_setting_bool(tctx, "samba4", false)) {
     565           1 :                 torture_comment(tctx, "skipping DsGetNCChanges test against Samba4\n");
     566           1 :                 return true;
     567             :         }
     568             : 
     569           0 :         ZERO_STRUCT(null_sid);
     570             : 
     571           0 :         for (i=0; i < ARRAY_SIZE(array); i++) {
     572           0 :                 torture_comment(tctx,
     573             :                                 "Testing DsGetNCChanges level %d\n",
     574             :                                 array[i].level);
     575             : 
     576           0 :                 r.in.bind_handle        = &priv->bind_handle;
     577           0 :                 r.in.level              = array[i].level;
     578           0 :                 r.out.level_out         = &level_out;
     579           0 :                 r.out.ctr               = &ctr;
     580             : 
     581           0 :                 switch (r.in.level) {
     582           0 :                 case 5:
     583           0 :                         nc.guid = GUID_zero();
     584           0 :                         nc.sid  = null_sid;
     585           0 :                         nc.dn   = priv->domain_obj_dn ? priv->domain_obj_dn : "";
     586             : 
     587           0 :                         r.in.req                                        = &req;
     588           0 :                         r.in.req->req5.destination_dsa_guid          = GUID_random();
     589           0 :                         r.in.req->req5.source_dsa_invocation_id      = GUID_zero();
     590           0 :                         r.in.req->req5.naming_context                        = &nc;
     591           0 :                         r.in.req->req5.highwatermark.tmp_highest_usn = 0;
     592           0 :                         r.in.req->req5.highwatermark.reserved_usn    = 0;
     593           0 :                         r.in.req->req5.highwatermark.highest_usn     = 0;
     594           0 :                         r.in.req->req5.uptodateness_vector           = NULL;
     595           0 :                         r.in.req->req5.replica_flags                 = 0;
     596           0 :                         if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "compression", false)) {
     597           0 :                                 r.in.req->req5.replica_flags         |= DRSUAPI_DRS_USE_COMPRESSION;
     598             :                         }
     599           0 :                         r.in.req->req5.max_object_count                      = 0;
     600           0 :                         r.in.req->req5.max_ndr_size                  = 0;
     601           0 :                         r.in.req->req5.extended_op                   = DRSUAPI_EXOP_NONE;
     602           0 :                         r.in.req->req5.fsmo_info                     = 0;
     603             : 
     604           0 :                         break;
     605           0 :                 case 8:
     606           0 :                         nc.guid = GUID_zero();
     607           0 :                         nc.sid  = null_sid;
     608           0 :                         nc.dn   = priv->domain_obj_dn ? priv->domain_obj_dn : "";
     609             : 
     610           0 :                         r.in.req                                        = &req;
     611           0 :                         r.in.req->req8.destination_dsa_guid          = GUID_random();
     612           0 :                         r.in.req->req8.source_dsa_invocation_id      = GUID_zero();
     613           0 :                         r.in.req->req8.naming_context                        = &nc;
     614           0 :                         r.in.req->req8.highwatermark.tmp_highest_usn = 0;
     615           0 :                         r.in.req->req8.highwatermark.reserved_usn    = 0;
     616           0 :                         r.in.req->req8.highwatermark.highest_usn     = 0;
     617           0 :                         r.in.req->req8.uptodateness_vector           = NULL;
     618           0 :                         r.in.req->req8.replica_flags                 = 0;
     619           0 :                         if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "compression", false)) {
     620           0 :                                 r.in.req->req8.replica_flags         |= DRSUAPI_DRS_USE_COMPRESSION;
     621             :                         }
     622           0 :                         if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "neighbour_writeable", true)) {
     623           0 :                                 r.in.req->req8.replica_flags         |= DRSUAPI_DRS_WRIT_REP;
     624             :                         }
     625           0 :                         r.in.req->req8.replica_flags                 |= DRSUAPI_DRS_INIT_SYNC
     626             :                                                                         | DRSUAPI_DRS_PER_SYNC
     627             :                                                                         | DRSUAPI_DRS_GET_ANC
     628             :                                                                         | DRSUAPI_DRS_NEVER_SYNCED
     629             :                                                                         ;
     630           0 :                         r.in.req->req8.max_object_count                      = 402;
     631           0 :                         r.in.req->req8.max_ndr_size                  = 402116;
     632           0 :                         r.in.req->req8.extended_op                   = DRSUAPI_EXOP_NONE;
     633           0 :                         r.in.req->req8.fsmo_info                     = 0;
     634           0 :                         r.in.req->req8.partial_attribute_set         = NULL;
     635           0 :                         r.in.req->req8.partial_attribute_set_ex              = NULL;
     636           0 :                         r.in.req->req8.mapping_ctr.num_mappings              = 0;
     637           0 :                         r.in.req->req8.mapping_ctr.mappings          = NULL;
     638             : 
     639           0 :                         break;
     640             :                 }
     641             : 
     642           0 :                 status = dcerpc_drsuapi_DsGetNCChanges_r(p->binding_handle, tctx, &r);
     643           0 :                 torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsGetNCChanges");
     644             :         }
     645             : 
     646           0 :         return true;
     647             : }
     648             : 
     649           0 : bool test_QuerySitesByCost(struct torture_context *tctx,
     650             :                            struct DsPrivate *priv)
     651             : {
     652             :         NTSTATUS status;
     653           0 :         struct dcerpc_pipe *p = priv->drs_pipe;
     654             :         struct drsuapi_QuerySitesByCost r;
     655             :         union drsuapi_QuerySitesByCostRequest req;
     656             : 
     657           0 :         const char *my_site = "Default-First-Site-Name";
     658           0 :         const char *remote_site1 = "smbtorture-nonexisting-site1";
     659           0 :         const char *remote_site2 = "smbtorture-nonexisting-site2";
     660             : 
     661           0 :         req.req1.site_from = talloc_strdup(tctx, my_site);
     662           0 :         req.req1.num_req = 2;
     663           0 :         req.req1.site_to = talloc_zero_array(tctx, const char *, 2);
     664           0 :         req.req1.site_to[0] = talloc_strdup(tctx, remote_site1);
     665           0 :         req.req1.site_to[1] = talloc_strdup(tctx, remote_site2);
     666           0 :         req.req1.flags = 0;
     667             : 
     668           0 :         r.in.bind_handle = &priv->bind_handle;
     669           0 :         r.in.level = 1;
     670           0 :         r.in.req = &req;
     671             : 
     672           0 :         status = dcerpc_drsuapi_QuerySitesByCost_r(p->binding_handle, tctx, &r);
     673           0 :         torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_QuerySitesByCost");
     674             : 
     675           0 :         if (W_ERROR_IS_OK(r.out.result)) {
     676           0 :                 torture_assert_werr_equal(tctx,
     677             :                                           r.out.ctr->ctr1.info[0].error_code, WERR_DS_OBJ_NOT_FOUND,
     678             :                                           "dcerpc_drsuapi_QuerySitesByCost");
     679           0 :                 torture_assert_werr_equal(tctx,
     680             :                                           r.out.ctr->ctr1.info[1].error_code, WERR_DS_OBJ_NOT_FOUND,
     681             :                                           "dcerpc_drsuapi_QuerySitesByCost expected error_code WERR_DS_OBJ_NOT_FOUND");
     682             : 
     683           0 :                 torture_assert_int_equal(tctx,
     684             :                                          r.out.ctr->ctr1.info[0].site_cost, -1,
     685             :                                          "dcerpc_drsuapi_QuerySitesByCost");
     686           0 :                 torture_assert_int_equal(tctx,
     687             :                                          r.out.ctr->ctr1.info[1].site_cost, -1,
     688             :                                          "dcerpc_drsuapi_QuerySitesByCost exptected site cost");
     689             :         }
     690             : 
     691           0 :         return true;
     692             : 
     693             : 
     694             : }
     695             : 
     696           0 : bool test_DsUnbind(struct dcerpc_pipe *p,
     697             :                    struct torture_context *tctx,
     698             :                    struct DsPrivate *priv)
     699             : {
     700             :         NTSTATUS status;
     701             :         struct drsuapi_DsUnbind r;
     702             : 
     703           0 :         r.in.bind_handle = &priv->bind_handle;
     704           0 :         r.out.bind_handle = &priv->bind_handle;
     705             : 
     706           0 :         torture_comment(tctx, "Testing DsUnbind\n");
     707             : 
     708           0 :         status = dcerpc_drsuapi_DsUnbind_r(p->binding_handle, tctx, &r);
     709           0 :         torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsUnbind");
     710             : 
     711           0 :         return true;
     712             : }
     713             : 
     714             : 
     715             : /**
     716             :  * Helper func to collect DC information for testing purposes.
     717             :  * This function is almost identical to test_DsGetDomainControllerInfo
     718             :  */
     719           2 : bool torture_rpc_drsuapi_get_dcinfo(struct torture_context *torture,
     720             :                                     struct DsPrivate *priv)
     721             : {
     722             :         NTSTATUS status;
     723           2 :         int32_t level_out = 0;
     724             :         struct drsuapi_DsGetDomainControllerInfo r;
     725             :         union drsuapi_DsGetDCInfoCtr ctr;
     726             :         int j, k;
     727           4 :         const char *names[] = {
     728           2 :                                 torture_join_dom_netbios_name(priv->join),
     729           2 :                                 torture_join_dom_dns_name(priv->join)};
     730             : 
     731           2 :         for (j=0; j < ARRAY_SIZE(names); j++) {
     732             :                 union drsuapi_DsGetDCInfoRequest req;
     733           2 :                 struct dcerpc_binding_handle *b = priv->drs_pipe->binding_handle;
     734           2 :                 r.in.bind_handle = &priv->bind_handle;
     735           2 :                 r.in.level = 1;
     736           2 :                 r.in.req = &req;
     737             : 
     738           2 :                 r.in.req->req1.domain_name = names[j];
     739           2 :                 r.in.req->req1.level = 2;
     740             : 
     741           2 :                 r.out.ctr = &ctr;
     742           2 :                 r.out.level_out = &level_out;
     743             : 
     744           2 :                 status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(b, torture, &r);
     745           2 :                 if (!NT_STATUS_IS_OK(status)) {
     746           0 :                         continue;
     747             :                 }
     748           2 :                 if (!W_ERROR_IS_OK(r.out.result)) {
     749             :                         /* If this was an error, we can't read the result structure */
     750           0 :                         continue;
     751             :                 }
     752             : 
     753           3 :                 for (k=0; k < r.out.ctr->ctr2.count; k++) {
     754           3 :                         if (strcasecmp_m(r.out.ctr->ctr2.array[k].netbios_name,
     755             :                                          torture_join_netbios_name(priv->join)) == 0) {
     756           2 :                                 priv->dcinfo = r.out.ctr->ctr2.array[k];
     757           2 :                                 return true;
     758             :                         }
     759             :                 }
     760             :         }
     761             : 
     762           0 :         return false;
     763             : }
     764             : 
     765             : /**
     766             :  * Common test case setup function to be used
     767             :  * in DRS suit of test when appropriate
     768             :  */
     769           4 : bool torture_drsuapi_tcase_setup_common(struct torture_context *tctx, struct DsPrivate *priv)
     770             : {
     771             :         NTSTATUS status;
     772           4 :         int rnd = rand() % 1000;
     773           4 :         char *name = talloc_asprintf(tctx, "%s%d", TEST_MACHINE_NAME, rnd);
     774             :         struct cli_credentials *machine_credentials;
     775             : 
     776           4 :         torture_assert(tctx, priv, "Invalid argument");
     777             : 
     778           4 :         torture_comment(tctx, "Create DRSUAPI pipe\n");
     779           4 :         status = torture_rpc_connection(tctx,
     780             :                                         &priv->drs_pipe,
     781             :                                         &ndr_table_drsuapi);
     782           4 :         torture_assert(tctx, NT_STATUS_IS_OK(status), "Unable to connect to DRSUAPI pipe");
     783             : 
     784           4 :         torture_comment(tctx, "About to join domain with name %s\n", name);
     785           4 :         priv->join = torture_join_domain(tctx, name, ACB_SVRTRUST,
     786             :                                          &machine_credentials);
     787           4 :         torture_assert(tctx, priv->join, "Failed to join as BDC");
     788             : 
     789           4 :         if (!test_DsBind(priv->drs_pipe, tctx, priv)) {
     790             :                 /* clean up */
     791           2 :                 torture_drsuapi_tcase_teardown_common(tctx, priv);
     792           2 :                 torture_fail(tctx, "Failed execute test_DsBind()");
     793             :         }
     794             : 
     795             :         /* try collect some information for testing */
     796           2 :         torture_rpc_drsuapi_get_dcinfo(tctx, priv);
     797             : 
     798           2 :         return true;
     799             : }
     800             : 
     801             : /**
     802             :  * Common test case teardown function to be used
     803             :  * in DRS suit of test when appropriate
     804             :  */
     805           4 : bool torture_drsuapi_tcase_teardown_common(struct torture_context *tctx, struct DsPrivate *priv)
     806             : {
     807           4 :         if (priv->join) {
     808           4 :                 torture_leave_domain(tctx, priv->join);
     809             :         }
     810             : 
     811           4 :         return true;
     812             : }
     813             : 
     814             : /**
     815             :  * Test case setup for DRSUAPI test case
     816             :  */
     817           3 : static bool torture_drsuapi_tcase_setup(struct torture_context *tctx, void **data)
     818             : {
     819             :         struct DsPrivate *priv;
     820             : 
     821           3 :         *data = priv = talloc_zero(tctx, struct DsPrivate);
     822             : 
     823           3 :         return torture_drsuapi_tcase_setup_common(tctx, priv);
     824             : }
     825             : 
     826             : /**
     827             :  * Test case tear-down for DRSUAPI test case
     828             :  */
     829           1 : static bool torture_drsuapi_tcase_teardown(struct torture_context *tctx, void *data)
     830             : {
     831             :         bool ret;
     832           1 :         struct DsPrivate *priv = talloc_get_type(data, struct DsPrivate);
     833             : 
     834           1 :         ret = torture_drsuapi_tcase_teardown_common(tctx, priv);
     835             : 
     836           1 :         talloc_free(priv);
     837           1 :         return ret;
     838             : }
     839             : 
     840             : /**
     841             :  * DRSUAPI test case implementation
     842             :  */
     843        2355 : void torture_rpc_drsuapi_tcase(struct torture_suite *suite)
     844             : {
     845             :         typedef bool (*run_func) (struct torture_context *test, void *tcase_data);
     846             : 
     847        2355 :         struct torture_tcase *tcase = torture_suite_add_tcase(suite, "drsuapi");
     848             : 
     849        2355 :         torture_tcase_set_fixture(tcase, torture_drsuapi_tcase_setup,
     850             :                                   torture_drsuapi_tcase_teardown);
     851             : 
     852             : #if 0
     853             :         test = torture_tcase_add_simple_test(tcase, "QuerySitesByCost", (run_func)test_QuerySitesByCost);
     854             : #endif
     855             : 
     856        2355 :         torture_tcase_add_simple_test(tcase, "DsGetDomainControllerInfo", (run_func)test_DsGetDomainControllerInfo);
     857             : 
     858        2355 :         torture_tcase_add_simple_test(tcase, "DsCrackNames", (run_func)test_DsCrackNames);
     859             : 
     860        2355 :         torture_tcase_add_simple_test(tcase, "DsWriteAccountSpn", (run_func)test_DsWriteAccountSpn);
     861             : 
     862        2355 :         torture_tcase_add_simple_test(tcase, "DsReplicaGetInfo", (run_func)test_DsReplicaGetInfo);
     863             : 
     864        2355 :         torture_tcase_add_simple_test(tcase, "DsReplicaSync", (run_func)test_DsReplicaSync);
     865             : 
     866        2355 :         torture_tcase_add_simple_test(tcase, "DsReplicaUpdateRefs", (run_func)test_DsReplicaUpdateRefs);
     867             : 
     868        2355 :         torture_tcase_add_simple_test(tcase, "DsGetNCChanges", (run_func)test_DsGetNCChanges);
     869        2355 : }

Generated by: LCOV version 1.13