s4/drs_util: 'net drs replicate' command implementation
authorKamen Mazdrashki <kamen.mazdrashki@postpath.com>
Sat, 13 Feb 2010 01:27:27 +0000 (03:27 +0200)
committerAnatoliy Atanasov <anatoliy.atanasov@postpath.com>
Wed, 24 Feb 2010 12:14:08 +0000 (14:14 +0200)
source4/utils/net/config.mk
source4/utils/net/drs/net_drs.c
source4/utils/net/drs/net_drs_replicate.c [new file with mode: 0644]

index e3dfb35ce0aba51bba8045d06d40bfae607340dd..ee1638cbdc59a71d6895493adf021d058a83b509 100644 (file)
@@ -15,7 +15,8 @@ PRIVATE_DEPENDENCIES = \
 net_drs_OBJ_FILES = $(addprefix $(utilssrcdir)/net/drs/,  \
                net_drs.o \
                net_drs_bind.o \
-               net_drs_kcc.o)
+               net_drs_kcc.o \
+               net_drs_replicate.o)
 
 $(eval $(call proto_header_template,$(utilssrcdir)/net/drs/net_drs_proto.h,$(net_drs_OBJ_FILES:.o=.c)))
 
index 87ebe0890af310d9da10090009d7232e272934b6..1ba6eea5765b1d2ef3a06093cd75e80d0fbbee28 100644 (file)
@@ -34,7 +34,9 @@
 static const struct net_functable net_drs_functable[] = {
        { "bind", "Display replication features for a domain controller\n", net_drs_bind_cmd, net_drs_bind_usage },
        { "kcc", "Forces the KCC to recalculate replication topology for a specified domain controller\n",
-                 net_drs_kcc_cmd, net_drs_kcc_usage },
+                       net_drs_kcc_cmd, net_drs_kcc_usage },
+       { "replicate", "Triggers replication event for the specified naming context between the source and destination domain controllers.\n",
+                       net_drs_replicate_cmd, net_drs_replicate_usage },
        { NULL, NULL }
 };
 
@@ -54,8 +56,9 @@ int net_drs_usage(struct net_context *ctx, int argc, const char **argv)
        d_printf("net drs <command> [options]\n");
        d_printf("\n");
        d_printf("Currently implemented commands:\n");
-       d_printf("  bind - Display DC replication features\n");
-       d_printf("  kcc - Forces the KCC to recalculate replication topology for a specified domain controller\n");
+       d_printf("  bind      - Display DC replication features\n");
+       d_printf("  kcc       - Forces the KCC to recalculate replication topology for a specified domain controller\n");
+       d_printf("  replicate - Triggers replication event for the specified naming context between the source and destination domain controllers.\n");
        return 0;
 }
 
diff --git a/source4/utils/net/drs/net_drs_replicate.c b/source4/utils/net/drs/net_drs_replicate.c
new file mode 100644 (file)
index 0000000..6c6cbfa
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Implements functions offered by repadmin.exe tool under Windows
+
+   Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2010
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net/net.h"
+#include "net_drs.h"
+#include "lib/ldb/include/ldb.h"
+#include "dsdb/samdb/samdb.h"
+
+
+/**
+ * Figure out what is the NDTS Settings objectGUID
+ * when DC_NAME is given
+ */
+static struct ldb_dn *
+net_drs_server_dn_from_dc_name(struct net_drs_context *drs_ctx,
+                               const char *dc_name)
+{
+       int ldb_err;
+       struct ldb_dn *dn;
+       struct ldb_dn *server_dn = NULL;
+       struct ldb_result *ldb_res;
+       const char *filter;
+       static const char *attrs[] = {
+               "objectGUID",
+               "name",
+               "dNSHostName",
+               NULL
+       };
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_new(drs_ctx);
+
+       /* Make DN for Sites container */
+       dn = ldb_msg_find_attr_as_dn(drs_ctx->ldap.ldb, mem_ctx, drs_ctx->ldap.rootdse, "dsServiceName");
+       NET_DRS_CHECK_GOTO(dn != NULL, failed, "RootDSE doesn't have dsServiceName!?\n");
+       if (!ldb_dn_remove_child_components(dn, 4)) {
+               d_printf("Failed to make DN for Sites container.\n");
+               goto failed;
+       }
+
+       /* search for Server in Sites container */
+       ldb_err = ldb_search(drs_ctx->ldap.ldb, mem_ctx, &ldb_res,
+                            dn, LDB_SCOPE_SUBTREE, attrs,
+                            "(&(objectCategory=server)(|(name=%1$s)(dNSHostName=%1$s)))",
+                            dc_name);
+       if (ldb_err != LDB_SUCCESS) {
+               d_printf("ldb_seach() failed with err: %d (%s); filter: (%s)",
+                        ldb_err, ldb_errstring(drs_ctx->ldap.ldb), filter);
+               goto failed;
+       }
+       if (ldb_res->count != 1) {
+               d_printf("ldb_search() should return exactly one record!");
+               goto failed;
+       }
+
+       server_dn = talloc_steal(drs_ctx, ldb_res->msgs[0]->dn);
+
+failed:
+       talloc_free(mem_ctx);
+       return server_dn;
+}
+
+
+/**
+ * Figure out what is the NDTS Settings objectGUID
+ * when DC_NAME is given
+ */
+static bool net_drs_ntds_guid_from_dc_name(struct net_drs_context *drs_ctx,
+                                          const char *dc_name,
+                                          struct GUID *_ntds_guid)
+{
+       int ldb_err;
+       struct ldb_dn *server_dn;
+       struct ldb_result *ldb_res;
+       const char *filter;
+       static const char *attrs[] = {
+               "objectGUID",
+               "msDS-portLDAP",
+               "name",
+               "objectCategory",
+               NULL
+       };
+       TALLOC_CTX *mem_ctx;
+
+       mem_ctx = talloc_new(drs_ctx);
+
+       /* resolve Server_DN for dc_name */
+       server_dn = net_drs_server_dn_from_dc_name(drs_ctx, dc_name);
+       if (!server_dn) {
+               d_printf("DSA object for %s could not be found.\n", dc_name);
+               goto failed;
+       }
+
+       /* move server_dn mem to local context */
+       server_dn = talloc_steal(mem_ctx, server_dn);
+
+       /* search ntdsDsa object under Server container */
+       ldb_err = ldb_search(drs_ctx->ldap.ldb, mem_ctx, &ldb_res,
+                            server_dn, LDB_SCOPE_ONELEVEL, attrs,
+                            "%s", "(|(objectCategory=nTDSDSA)(objectCategory=nTDSDSARO))");
+       if (ldb_err != LDB_SUCCESS) {
+               d_printf("ldb_seach() failed with err: %d (%s); filter: (%s)",
+                        ldb_err, ldb_errstring(drs_ctx->ldap.ldb), filter);
+               goto failed;
+       }
+       if (ldb_res->count != 1) {
+               d_printf("ldb_search() should return exactly one record!");
+               goto failed;
+       }
+
+       *_ntds_guid =  samdb_result_guid(ldb_res->msgs[0], "objectGUID");
+
+       talloc_free(mem_ctx);
+       return true;
+
+failed:
+       talloc_free(mem_ctx);
+       return false;
+}
+
+/**
+ * Sends DsReplicaSync to dc_name_dest to
+ * replicate naming context nc_dn_str from
+ * server with ntds_guid_src GUID
+ */
+static bool net_drs_replicate_sync_nc(struct net_drs_context *drs_ctx,
+                                     struct GUID ntds_guid_src,
+                                     const char *nc_dn_str,
+                                     uint32_t options)
+{
+       NTSTATUS status;
+       struct net_drs_connection *drs_conn;
+       struct drsuapi_DsReplicaSync req;
+       union drsuapi_DsReplicaSyncRequest sync_req;
+       struct drsuapi_DsReplicaObjectIdentifier nc;
+
+       /* use already opened connection */
+       drs_conn = drs_ctx->drs_conn;
+
+       /* construct naming context object */
+       ZERO_STRUCT(nc);
+       nc.dn = nc_dn_str;
+
+       /* construct request object for DsReplicaSync */
+       req.in.bind_handle                      = &drs_conn->bind_handle;
+       req.in.level                            = 1;
+       req.in.req                              = &sync_req;
+       req.in.req->req1.naming_context         = &nc;
+       req.in.req->req1.options                = options;
+       req.in.req->req1.source_dsa_dns         = NULL;
+       req.in.req->req1.source_dsa_guid        = ntds_guid_src;
+
+       /* send DsReplicaSync request */
+       status = dcerpc_drsuapi_DsReplicaSync(drs_conn->drs_pipe, drs_ctx, &req);
+       if (!NT_STATUS_IS_OK(status)) {
+               const char *errstr = nt_errstr(status);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+                       errstr = dcerpc_errstr(drs_ctx, drs_conn->drs_pipe->last_fault_code);
+               }
+               d_printf("DsReplicaSync failed - %s.\n", errstr);
+               return false;
+       } else if (!W_ERROR_IS_OK(req.out.result)) {
+               d_printf("DsReplicaSync failed - %s.\n", win_errstr(req.out.result));
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ * 'net drs replicate' command entry point
+ */
+int net_drs_replicate_cmd(struct net_context *ctx, int argc, const char **argv)
+{
+       bool bret;
+       struct net_drs_context *drs_ctx;
+       struct GUID ntds_guid_src;
+       const char *dc_name_dest;
+       const char *dc_name_src;
+       const char *nc_dn_str;
+
+       /* only one arg expected */
+       if (argc != 3) {
+               return net_drs_replicate_usage(ctx, argc, argv);
+       }
+
+       dc_name_dest = argv[0];
+       dc_name_src = argv[1];
+       nc_dn_str = argv[2];
+
+       if (!net_drs_create_context(ctx, dc_name_dest, &drs_ctx)) {
+               return -1;
+       }
+
+       /* Resolve source DC_NAME to its NDTS Settings GUID */
+       if (!net_drs_ntds_guid_from_dc_name(drs_ctx, dc_name_src, &ntds_guid_src)) {
+               d_printf("Error: DSA object for %s could not be found.\n", dc_name_src);
+               goto failed;
+       }
+
+       /* Synchronize given Naming Context */
+       bret = net_drs_replicate_sync_nc(drs_ctx,
+                                        ntds_guid_src, nc_dn_str,
+                                        DRSUAPI_DRS_WRIT_REP);
+       if (!bret) {
+               goto failed;
+       }
+
+       d_printf("Replicate from %s to %s was successful.\n", dc_name_src, drs_ctx->dc_name);
+
+       talloc_free(drs_ctx);
+       return 0;
+
+failed:
+       d_printf("Replicate terminated with errors.\n");
+       talloc_free(drs_ctx);
+       return -1;
+}
+
+/**
+ * 'net drs replicate' usage
+ */
+int net_drs_replicate_usage(struct net_context *ctx, int argc, const char **argv)
+{
+       d_printf("net drs replicate <Dest_DC_NAME> <Src_DC_NAME> <Naming Context>\n");
+       return 0;
+}