summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/dsdb/kcc/kcc_connection.c2
-rw-r--r--source4/dsdb/kcc/kcc_topology.c815
2 files changed, 817 insertions, 0 deletions
diff --git a/source4/dsdb/kcc/kcc_connection.c b/source4/dsdb/kcc/kcc_connection.c
index d0d549dc1f..e78a615344 100644
--- a/source4/dsdb/kcc/kcc_connection.c
+++ b/source4/dsdb/kcc/kcc_connection.c
@@ -175,6 +175,8 @@ struct kcc_connection_list *kccsrv_find_connections(struct kccsrv_service *s,
const char *attrs[] = { "objectGUID", "fromServer", NULL };
struct kcc_connection_list *list;
+ kcctpl_test(s->samdb);
+
base_dn = samdb_ntds_settings_dn(s->samdb);
if (!base_dn) {
DEBUG(0, ("failed to find our own NTDS settings DN\n"));
diff --git a/source4/dsdb/kcc/kcc_topology.c b/source4/dsdb/kcc/kcc_topology.c
index ea303b9076..4d3953492d 100644
--- a/source4/dsdb/kcc/kcc_topology.c
+++ b/source4/dsdb/kcc/kcc_topology.c
@@ -25,14 +25,27 @@
#include "lib/messaging/irpc.h"
#include "librpc/gen_ndr/ndr_misc.h"
+#define FLAG_CR_NTDS_NC 0x00000001
#define FLAG_CR_NTDS_DOMAIN 0x00000002
+#define NTDSCONN_OPT_IS_GENERATED 0x00000001
+#define NTDSCONN_OPT_TWOWAY_SYNC 0x00000002
+#define NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT 0x00000004
+#define NTDSCONN_OPT_USE_NOTIFY 0x00000008
+#define NTDSCONN_OPT_DISABLE_INTERSITE_COMPRESSION 0x00000010
+#define NTDSCONN_OPT_USER_OWNED_SCHEDULE 0x00000020
+#define NTDSCONN_OPT_RODC_TOPOLOGY 0x00000040
+
#define NTDSDSA_OPT_IS_GC 0x00000001
#define NTDSSETTINGS_OPT_IS_TOPL_DETECT_STALE_DISABLED 0x00000008
#define NTDSSETTINGS_OPT_IS_RAND_BH_SELECTION_DISABLED 0x00000100
#define NTDSSETTINGS_OPT_W2K3_BRIDGES_REQUIRED 0x00001000
+#define NTDSSITELINK_OPT_USE_NOTIFY 0x00000001
+#define NTDSSITELINK_OPT_TWOWAY_SYNC 0x00000002
+#define NTDSSITELINK_OPT_DISABLE_COMPRESSION 0x00000004
+
#define NTDSTRANSPORT_OPT_BRIDGES_REQUIRED 0x00000002
#define DS_BEHAVIOR_WIN2008 3
@@ -369,6 +382,26 @@ static bool kcctpl_guid_list_contains(struct GUID_list list, struct GUID guid)
}
/**
+ * search for an occurrence of an ldb_message inside a list of ldb_messages,
+ * based on the ldb_message's DN.
+ */
+static bool kcctpl_message_list_contains_dn(struct message_list list,
+ struct ldb_dn *dn)
+{
+ uint32_t i;
+
+ for (i = 0; i < list.count; i++) {
+ struct ldb_message *message = &list.data[i];
+
+ if (ldb_dn_compare(message->dn, dn) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
* get the Transports DN
* (CN=Inter-Site Transports,CN=Sites,CN=Configuration,DC=<domain>).
*/
@@ -2666,3 +2699,785 @@ static NTSTATUS kcctpl_get_spanning_tree_edges(struct ldb_context *ldb,
talloc_free(tmp_ctx);
return NT_STATUS_OK;
}
+
+/**
+ * creat an nTDSConnection object with the given parameters if one does not
+ * already exist.
+ */
+static NTSTATUS kcctpl_create_connection(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *cross_ref,
+ struct ldb_message *r_bridgehead,
+ struct ldb_message *transport,
+ struct ldb_message *l_bridgehead,
+ struct kcctpl_repl_info repl_info,
+ uint8_t schedule[84],
+ bool detect_failed_dcs,
+ bool partial_replica_okay,
+ struct GUID_list *_keep_connections)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *r_site_dn, *l_site_dn, *servers_dn;
+ bool ok;
+ struct GUID r_site_guid, l_site_guid;
+ int ret;
+ struct message_list r_bridgeheads_all, l_bridgeheads_all,
+ r_bridgeheads_available, l_bridgeheads_available;
+ NTSTATUS status;
+ struct ldb_result *res;
+ const char * const attrs[] = { "objectGUID", "parent", "fromServer",
+ "transportType", "schedule", "options",
+ "enabledConnection", NULL };
+ uint32_t i, valid_connections;
+ struct GUID_list keep_connections;
+
+ tmp_ctx = talloc_new(ldb);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ r_site_dn = ldb_dn_copy(tmp_ctx, r_bridgehead->dn);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(r_site_dn, tmp_ctx);
+
+ ok = ldb_dn_remove_child_components(r_site_dn, 3);
+ if (!ok) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dsdb_find_guid_by_dn(ldb, r_site_dn, &r_site_guid);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, (__location__ ": failed to find objectGUID for object "
+ "%s: %s\n", ldb_dn_get_linearized(r_site_dn),
+ ldb_strerror(ret)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ l_site_dn = ldb_dn_copy(tmp_ctx, l_bridgehead->dn);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(l_site_dn, tmp_ctx);
+
+ ok = ldb_dn_remove_child_components(l_site_dn, 3);
+ if (!ok) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = dsdb_find_guid_by_dn(ldb, l_site_dn, &l_site_guid);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, (__location__ ": failed to find objectGUID for object "
+ "%s: %s\n", ldb_dn_get_linearized(l_site_dn),
+ ldb_strerror(ret)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = kcctpl_get_all_bridgehead_dcs(ldb, tmp_ctx,
+ r_site_guid, cross_ref,
+ transport, partial_replica_okay,
+ false, &r_bridgeheads_all);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to get all bridgehead DCs: "
+ "%s\n", nt_errstr(status)));
+ return status;
+ }
+
+ status = kcctpl_get_all_bridgehead_dcs(ldb, tmp_ctx,
+ r_site_guid, cross_ref,
+ transport, partial_replica_okay,
+ detect_failed_dcs,
+ &r_bridgeheads_available);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to get all bridgehead DCs: "
+ "%s\n", nt_errstr(status)));
+ return status;
+ }
+
+ status = kcctpl_get_all_bridgehead_dcs(ldb, tmp_ctx,
+ l_site_guid, cross_ref,
+ transport, partial_replica_okay,
+ false, &l_bridgeheads_all);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to get all bridgehead DCs: "
+ "%s\n", nt_errstr(status)));
+ return status;
+ }
+
+ status = kcctpl_get_all_bridgehead_dcs(ldb, tmp_ctx,
+ l_site_guid, cross_ref,
+ transport, partial_replica_okay,
+ detect_failed_dcs,
+ &l_bridgeheads_available);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to get all bridgehead DCs: "
+ "%s\n", nt_errstr(status)));
+ return status;
+ }
+
+ servers_dn = samdb_sites_dn(ldb, tmp_ctx);
+ if (!servers_dn) {
+ DEBUG(1, (__location__ ": failed to find our own Sites DN\n"));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ ok = ldb_dn_add_child_fmt(servers_dn, "CN=Servers");
+ if (!ok) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res, servers_dn, LDB_SCOPE_SUBTREE,
+ attrs, "objectClass=nTDSConnection");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, (__location__ ": failed to find nTDSConnection "
+ "objects: %s\n", ldb_strerror(ret)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ for (i = 0; i < res->count; i++) {
+ struct ldb_message *connection;
+ struct ldb_dn *parent_dn, *from_server;
+
+ connection = res->msgs[i];
+
+ parent_dn = ldb_dn_get_parent(tmp_ctx, connection->dn);
+ if (!parent_dn) {
+ DEBUG(1, (__location__ ": failed to get parent DN of "
+ "%s\n",
+ ldb_dn_get_linearized(connection->dn)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ from_server = samdb_result_dn(ldb, tmp_ctx, connection,
+ "fromServer", NULL);
+ if (!from_server) {
+ DEBUG(1, (__location__ ": failed to find fromServer "
+ "attribute of object %s\n",
+ ldb_dn_get_linearized(connection->dn)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (kcctpl_message_list_contains_dn(l_bridgeheads_all,
+ parent_dn) &&
+ kcctpl_message_list_contains_dn(r_bridgeheads_all,
+ from_server)) {
+ uint64_t conn_opts;
+ /* TODO: initialize conn_schedule from connection */
+ uint8_t conn_schedule[84];
+ struct ldb_dn *conn_transport_type;
+
+ conn_opts = samdb_result_int64(connection,
+ "options", 0);
+
+ conn_transport_type = samdb_result_dn(ldb, tmp_ctx,
+ connection,
+ "transportType",
+ NULL);
+ if (!conn_transport_type) {
+ DEBUG(1, (__location__ ": failed to find "
+ "transportType attribute of object "
+ "%s\n",
+ ldb_dn_get_linearized(connection->dn)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if ((conn_opts & NTDSCONN_OPT_IS_GENERATED) &&
+ !(conn_opts & NTDSCONN_OPT_RODC_TOPOLOGY) &&
+ ldb_dn_compare(conn_transport_type,
+ transport->dn) == 0) {
+ if (!(conn_opts & NTDSCONN_OPT_USER_OWNED_SCHEDULE) &&
+ memcmp(&conn_schedule, &schedule, 84) != 0) {
+ /* TODO: perform an originating update
+ to set conn!schedule to schedule */
+ }
+
+ if ((conn_opts & NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT) &&
+ (conn_opts & NTDSCONN_OPT_USE_NOTIFY)) {
+ if (!(repl_info.options & NTDSSITELINK_OPT_USE_NOTIFY)) {
+ /* TODO: perform an originating
+ update to clear bits
+ NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT
+ and NTDSCONN_OPT_USE_NOTIFY
+ in conn!options */
+ }
+ } else if (repl_info.options & NTDSSITELINK_OPT_USE_NOTIFY) {
+ /* TODO: perform an originating update
+ to set bits
+ NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT
+ and NTDSCONN_OPT_USE_NOTIFY in
+ conn!options */
+ }
+
+ if (conn_opts & NTDSCONN_OPT_TWOWAY_SYNC) {
+ if (!(repl_info.options & NTDSSITELINK_OPT_TWOWAY_SYNC)) {
+ /* TODO: perform an originating
+ update to clear bit
+ NTDSCONN_OPT_TWOWAY_SYNC in
+ conn!options. */
+ }
+ } else if (repl_info.options & NTDSSITELINK_OPT_TWOWAY_SYNC) {
+ /* TODO: perform an originating update
+ to set bit NTDSCONN_OPT_TWOWAY_SYNC
+ in conn!options. */
+ }
+
+ if (conn_opts & NTDSCONN_OPT_DISABLE_INTERSITE_COMPRESSION) {
+ if (!(repl_info.options & NTDSSITELINK_OPT_DISABLE_COMPRESSION)) {
+ /* TODO: perform an originating
+ update to clear bit
+ NTDSCONN_OPT_DISABLE_INTERSITE_COMPRESSION
+ in conn!options. */
+ }
+ } else if (repl_info.options & NTDSSITELINK_OPT_DISABLE_COMPRESSION) {
+ /* TODO: perform an originating update
+ to set bit
+ NTDSCONN_OPT_DISABLE_INTERSITE_COMPRESSION
+ in conn!options. */
+ }
+ }
+ }
+ }
+
+ ZERO_STRUCT(keep_connections);
+
+ valid_connections = 0;
+ for (i = 0; i < res->count; i++) {
+ struct ldb_message *connection;
+ struct ldb_dn *parent_dn, *from_server;
+
+ connection = res->msgs[i];
+
+ parent_dn = ldb_dn_get_parent(tmp_ctx, connection->dn);
+ if (!parent_dn) {
+ DEBUG(1, (__location__ ": failed to get parent DN of "
+ "%s\n",
+ ldb_dn_get_linearized(connection->dn)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ from_server = samdb_result_dn(ldb, tmp_ctx, connection,
+ "fromServer", NULL);
+ if (!from_server) {
+ DEBUG(1, (__location__ ": failed to find fromServer "
+ "attribute of object %s\n",
+ ldb_dn_get_linearized(connection->dn)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (kcctpl_message_list_contains_dn(l_bridgeheads_all,
+ parent_dn) &&
+ kcctpl_message_list_contains_dn(r_bridgeheads_all,
+ from_server)) {
+ uint64_t conn_opts;
+ struct ldb_dn *conn_transport_type;
+
+ conn_opts = samdb_result_int64(connection,
+ "options", 0);
+
+ conn_transport_type = samdb_result_dn(ldb, tmp_ctx,
+ connection,
+ "transportType",
+ NULL);
+ if (!conn_transport_type) {
+ DEBUG(1, (__location__ ": failed to find "
+ "transportType attribute of object "
+ "%s\n",
+ ldb_dn_get_linearized(connection->dn)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if ((!(conn_opts & NTDSCONN_OPT_IS_GENERATED) ||
+ ldb_dn_compare(conn_transport_type,
+ transport->dn) == 0) &&
+ !(conn_opts & NTDSCONN_OPT_RODC_TOPOLOGY)) {
+ struct GUID r_guid, l_guid, conn_guid;
+ bool failed_state_r, failed_state_l;
+
+ ret = dsdb_find_guid_by_dn(ldb, from_server,
+ &r_guid);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, (__location__ ": failed to "
+ "find GUID for object %s\n",
+ ldb_dn_get_linearized(from_server)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ ret = dsdb_find_guid_by_dn(ldb, parent_dn,
+ &l_guid);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, (__location__ ": failed to "
+ "find GUID for object %s\n",
+ ldb_dn_get_linearized(parent_dn)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = kcctpl_bridgehead_dc_failed(ldb,
+ r_guid,
+ detect_failed_dcs,
+ &failed_state_r);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to "
+ "check if bridgehead DC has "
+ "failed: %s\n",
+ nt_errstr(status)));
+
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ status = kcctpl_bridgehead_dc_failed(ldb,
+ l_guid,
+ detect_failed_dcs,
+ &failed_state_l);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to "
+ "check if bridgehead DC has "
+ "failed: %s\n",
+ nt_errstr(status)));
+
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (!failed_state_r && !failed_state_l) {
+ valid_connections++;
+ }
+
+ conn_guid = samdb_result_guid(connection,
+ "objectGUID");
+
+ if (!kcctpl_guid_list_contains(keep_connections,
+ conn_guid)) {
+ struct GUID *new_data;
+
+ new_data = talloc_realloc(tmp_ctx,
+ keep_connections.data,
+ struct GUID,
+ keep_connections.count + 1);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(new_data,
+ tmp_ctx);
+ new_data[keep_connections.count] = conn_guid;
+ keep_connections.data = new_data;
+ keep_connections.count++;
+ }
+ }
+ }
+ }
+
+ if (valid_connections == 0) {
+ uint64_t opts = NTDSCONN_OPT_IS_GENERATED;
+ struct GUID new_guid, *new_data;
+
+ if (repl_info.options & NTDSSITELINK_OPT_USE_NOTIFY) {
+ opts |= NTDSCONN_OPT_OVERRIDE_NOTIFY_DEFAULT;
+ opts |= NTDSCONN_OPT_USE_NOTIFY;
+ }
+
+ if (repl_info.options & NTDSSITELINK_OPT_TWOWAY_SYNC) {
+ opts |= NTDSCONN_OPT_TWOWAY_SYNC;
+ }
+
+ if (repl_info.options & NTDSSITELINK_OPT_DISABLE_COMPRESSION) {
+ opts |= NTDSCONN_OPT_DISABLE_INTERSITE_COMPRESSION;
+ }
+
+ /* perform an originating update to create a new nTDSConnection
+ * object cn that is:
+ *
+ * - child of l_bridgehead
+ * - cn!enabledConnection = true
+ * - cn!options = opts
+ * - cn!transportType = t
+ * - cn!fromServer = r_bridgehead
+ * - cn!schedule = schedule
+ */
+
+ /* TODO: what should be the new connection's GUID? */
+ new_guid = GUID_random();
+
+ new_data = talloc_realloc(tmp_ctx, keep_connections.data,
+ struct GUID,
+ keep_connections.count + 1);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(new_data, tmp_ctx);
+ new_data[keep_connections.count] = new_guid;
+ keep_connections.data = new_data;
+ keep_connections.count++;
+ }
+
+ talloc_steal(mem_ctx, keep_connections.data);
+ *_keep_connections = keep_connections;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+/**
+ * construct an NC replica graph for the NC identified by the given 'cross_ref',
+ * then create any additional nTDSConnection objects required.
+ */
+static NTSTATUS kcctpl_create_connections(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct kcctpl_graph *graph,
+ struct ldb_message *cross_ref,
+ bool detect_failed_dcs,
+ struct GUID_list keep_connections,
+ bool *_found_failed_dcs,
+ bool *_connected)
+{
+ bool connected, found_failed_dcs, partial_replica_okay, rodc;
+ NTSTATUS status;
+ struct ldb_message *site;
+ TALLOC_CTX *tmp_ctx;
+ struct GUID site_guid;
+ struct kcctpl_vertex *site_vertex;
+ uint32_t component_count, i;
+ struct kcctpl_multi_edge_list st_edge_list;
+ struct ldb_dn *transports_dn;
+ const char * const attrs[] = { "bridgeheadServerListBL", "name",
+ "transportAddressAttribute", NULL };
+
+ connected = true;
+
+ status = kcctpl_color_vertices(ldb, graph, cross_ref, detect_failed_dcs,
+ &found_failed_dcs);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to color vertices: %s\n",
+ nt_errstr(status)));
+
+ return status;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ site = kcctpl_local_site(ldb, tmp_ctx);
+ if (!site) {
+ DEBUG(1, (__location__ ": failed to find our own local DC's "
+ "site\n"));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ site_guid = samdb_result_guid(site, "objectGUID");
+
+ site_vertex = kcctpl_find_vertex_by_guid(graph, site_guid);
+ if (!site_vertex) {
+ DEBUG(1, (__location__ ": failed to find vertex %s\n",
+ GUID_string(tmp_ctx, &site_guid)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (site_vertex->color == WHITE) {
+ *_found_failed_dcs = found_failed_dcs;
+ *_connected = true;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ status = kcctpl_get_spanning_tree_edges(ldb, tmp_ctx, graph,
+ &component_count,
+ &st_edge_list);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed get spanning tree edges: %s\n",
+ nt_errstr(status)));
+
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (component_count > 1) {
+ connected = false;
+ }
+
+ partial_replica_okay = (site_vertex->color == BLACK);
+
+ transports_dn = kcctpl_transports_dn(ldb, tmp_ctx);
+ if (!transports_dn) {
+ DEBUG(1, (__location__ ": failed to find our own Inter-Site "
+ "Transports DN\n"));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ rodc = samdb_rodc(ldb);
+
+ for (i = 0; i < st_edge_list.count; i++) {
+ struct kcctpl_multi_edge *edge;
+ struct GUID other_site_id;
+ struct kcctpl_vertex *other_site_vertex;
+ struct ldb_result *res;
+ int ret;
+ struct ldb_message *transport, *r_bridgehead, *l_bridgehead;
+ uint8_t schedule[84];
+ uint32_t first_available, j, interval;
+
+ edge = &st_edge_list.data[i];
+
+ if (edge->directed && !GUID_equal(&edge->vertex_ids.data[1],
+ &site_vertex->id)) {
+ continue;
+ }
+
+ if (GUID_equal(&edge->vertex_ids.data[0], &site_vertex->id)) {
+ other_site_id = edge->vertex_ids.data[1];
+ } else {
+ other_site_id = edge->vertex_ids.data[0];
+ }
+
+ other_site_vertex = kcctpl_find_vertex_by_guid(graph,
+ other_site_id);
+ if (!other_site_vertex) {
+ DEBUG(1, (__location__ ": failed to find vertex %s\n",
+ GUID_string(tmp_ctx, &other_site_id)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res, transports_dn,
+ LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=interSiteTransport)"
+ "(objectGUID=%s))", GUID_string(tmp_ctx,
+ &edge->type));
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, (__location__ ": failed to find "
+ "interSiteTransport object %s: %s\n",
+ GUID_string(tmp_ctx, &edge->type),
+ ldb_strerror(ret)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (res->count == 0) {
+ DEBUG(1, (__location__ ": failed to find "
+ "interSiteTransport object %s\n",
+ GUID_string(tmp_ctx, &edge->type)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ transport = res->msgs[0];
+
+ status = kcctpl_get_bridgehead_dc(ldb, tmp_ctx,
+ other_site_vertex->id,
+ cross_ref, transport,
+ partial_replica_okay,
+ detect_failed_dcs,
+ &r_bridgehead);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to get a bridgehead "
+ "DC: %s\n", nt_errstr(status)));
+
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (rodc) {
+ /* TODO: l_bridgehad = nTDSDSA of local DC */
+ } else {
+ status = kcctpl_get_bridgehead_dc(ldb, tmp_ctx,
+ site_vertex->id,
+ cross_ref, transport,
+ partial_replica_okay,
+ detect_failed_dcs,
+ &l_bridgehead);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to get a "
+ "bridgehead DC: %s\n",
+ nt_errstr(status)));
+
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+
+ ZERO_ARRAY(schedule);
+ first_available = 84;
+ interval = edge->repl_info.interval / 15;
+ for (j = 0; j < 84; j++) {
+ if (edge->repl_info.schedule[j] == 1) {
+ first_available = j;
+ break;
+ }
+ }
+ for (j = first_available; j < 84; j += interval) {
+ schedule[j] = 1;
+ }
+
+ status = kcctpl_create_connection(ldb, mem_ctx, cross_ref,
+ r_bridgehead, transport,
+ l_bridgehead, edge->repl_info,
+ schedule, detect_failed_dcs,
+ partial_replica_okay,
+ &keep_connections);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to create a "
+ "connection: %s\n", nt_errstr(status)));
+
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+
+ *_found_failed_dcs = found_failed_dcs;
+ *_connected = connected;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+/**
+ * computes an NC replica graph for each NC replica that "should be present" on
+ * the local DC or "is present" on any DC in the same site as the local DC. for
+ * each edge directed to an NC replica on such a DC from an NC replica on a DC
+ * in another site, the KCC creates and nTDSConnection object to imply that edge
+ * if one does not already exist.
+ */
+static NTSTATUS kcctpl_create_intersite_connections(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct GUID_list *_keep_connections,
+ bool *_all_connected)
+{
+ struct GUID_list keep_connections;
+ bool all_connected;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *partitions_dn;
+ struct ldb_result *res;
+ const char * const attrs[] = { "enabled", "systemFlags", "nCName",
+ NULL };
+ int ret;
+ uint32_t i;
+
+ all_connected = true;
+
+ ZERO_STRUCT(keep_connections);
+
+ tmp_ctx = talloc_new(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ partitions_dn = samdb_partitions_dn(ldb, tmp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY_AND_FREE(partitions_dn, tmp_ctx);
+
+ ret = ldb_search(ldb, tmp_ctx, &res, partitions_dn, LDB_SCOPE_ONELEVEL,
+ attrs, "objectClass=crossRef");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, (__location__ ": failed to find crossRef objects: "
+ "%s\n", ldb_strerror(ret)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ for (i = 0; i < res->count; i++) {
+ struct ldb_message *cross_ref;
+ unsigned int cr_enabled;
+ int64_t cr_flags;
+ struct kcctpl_graph *graph;
+ bool found_failed_dc, connected;
+ NTSTATUS status;
+
+ cross_ref = res->msgs[i];
+ cr_enabled = samdb_result_uint(cross_ref, "enabled", -1);
+ cr_flags = samdb_result_int64(cross_ref, "systemFlags", 0);
+ if ((cr_enabled == 0) || !(cr_flags & FLAG_CR_NTDS_NC)) {
+ continue;
+ }
+
+ status = kcctpl_setup_graph(ldb, tmp_ctx, &graph);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to create a graph: "
+ "%s\n", nt_errstr(status)));
+
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ status = kcctpl_create_connections(ldb, mem_ctx, graph,
+ cross_ref, true,
+ keep_connections,
+ &found_failed_dc,
+ &connected);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to create "
+ "connections: %s\n", nt_errstr(status)));
+
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (!connected) {
+ all_connected = false;
+
+ if (found_failed_dc) {
+ status = kcctpl_create_connections(ldb, mem_ctx,
+ graph,
+ cross_ref,
+ true,
+ keep_connections,
+ &found_failed_dc,
+ &connected);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, (__location__ ": failed to "
+ "create connections: %s\n",
+ nt_errstr(status)));
+
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+ }
+ }
+
+ *_keep_connections = keep_connections;
+ *_all_connected = all_connected;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS kcctpl_test(struct ldb_context *ldb)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+ struct GUID_list keep;
+ bool all_connected;
+
+ DEBUG(0, ("testing kcctpl_create_intersite_connections\n"));
+ status = kcctpl_create_intersite_connections(ldb, tmp_ctx, &keep,
+ &all_connected);
+ DEBUG(4, ("%s\n", nt_errstr(status)));
+ if (NT_STATUS_IS_OK(status)) {
+ uint32_t i;
+
+ DEBUG(4, ("all_connected=%d, %d GUIDs returned\n",
+ all_connected, keep.count));
+
+ for (i = 0; i < keep.count; i++) {
+ DEBUG(4, ("GUID %d: %s\n", i + 1,
+ GUID_string(tmp_ctx, &keep.data[i])));
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return status;
+}