2 Unix SMB/CIFS implementation.
6 Copyright (C) CrÃstian Deives 2010
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "dsdb/samdb/samdb.h"
25 #include "lib/messaging/irpc.h"
26 #include "librpc/gen_ndr/ndr_misc.h"
28 #define NTDSTRANSPORT_OPT_IGNORE_SCHEDULES 0x00000001
29 #define NTDSTRANSPORT_OPT_BRIDGES_REQUIRED 0x00000002
31 /** replication parameters of a graph edge */
32 struct kcctpl_repl_info {
39 /** color of a vertex */
40 enum kcctpl_color { RED, BLACK, WHITE };
42 /** a GUID array list */
48 /** a vertex in the site graph */
49 struct kcctpl_vertex {
51 struct GUID_list edge_ids;
52 enum kcctpl_color color;
53 struct GUID_list accept_red_red;
54 struct GUID_list accept_black;
55 struct kcctpl_repl_info repl_info;
63 struct GUID component_id;
67 /** fully connected subgraph of vertices */
68 struct kcctpl_multi_edge {
70 struct GUID_list vertex_ids;
72 struct kcctpl_repl_info repl_info;
76 /** set of transitively connected kcc_multi_edge's. all edges within the set
77 * have the same type. */
78 struct kcctpl_multi_edge_set {
80 struct GUID_list edge_ids;
83 /** a vertices array list */
84 struct kcctpl_vertex_list {
85 struct kcctpl_vertex *data;
89 /** an edges linked list */
90 struct kcctpl_multi_edge_list {
91 struct kcctpl_multi_edge *data;
95 /** an edge sets linked list */
96 struct kcctpl_multi_edge_set_list {
97 struct kcctpl_multi_edge_set *data;
102 struct kcctpl_graph {
103 struct kcctpl_vertex_list vertices;
104 struct kcctpl_multi_edge_list edges;
105 struct kcctpl_multi_edge_set_list edge_sets;
108 /** path found in the graph between two non-white vertices */
109 struct kcctpl_internal_edge {
110 struct GUID v1id, v2id;
112 struct kcctpl_repl_info repl_info;
117 * find a graph edge based on its GUID.
119 static struct kcctpl_multi_edge *kcctpl_find_edge_by_guid(struct kcctpl_graph *graph,
124 for (i = 0; i < graph->edges.count; i++) {
125 if (GUID_compare(&graph->edges.data[i].id, &guid) == 0) {
126 return &graph->edges.data[i];
132 * create a kcctpl_graph instance.
134 static NTSTATUS kcctpl_create_graph(TALLOC_CTX *mem_ctx,
135 struct GUID_list *guids,
136 struct kcctpl_graph **_graph)
138 struct kcctpl_graph *graph;
141 graph = talloc_zero(mem_ctx, struct kcctpl_graph);
142 NT_STATUS_HAVE_NO_MEMORY(graph);
144 graph->vertices.count = guids->count;
145 graph->vertices.data = talloc_zero_array(graph, struct kcctpl_vertex,
147 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(graph->vertices.data, graph);
149 qsort(guids->data, guids->count, sizeof(struct GUID),
150 QSORT_CAST GUID_compare);
152 for (i = 0; i < guids->count; i++) {
153 graph->vertices.data[i].id = guids->data[i];
161 * create a kcctpl_multi_edge instance.
163 static NTSTATUS kcctpl_create_edge(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
165 struct ldb_message *site_link,
166 struct kcctpl_multi_edge **_edge)
168 struct kcctpl_multi_edge *edge;
170 struct ldb_result *res;
171 const char * const attrs[] = { "siteList", NULL };
173 struct ldb_message_element *el;
177 edge = talloc(mem_ctx, struct kcctpl_multi_edge);
178 NT_STATUS_HAVE_NO_MEMORY(edge);
180 edge->id = samdb_result_guid(site_link, "objectGUID");
182 tmp_ctx = talloc_new(mem_ctx);
183 ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_BASE, attrs,
184 "objectGUID=%s", GUID_string(tmp_ctx, &edge->id));
185 if (ret != LDB_SUCCESS) {
186 DEBUG(0, ("failed to find siteLink object %s: %s\n",
187 GUID_string(tmp_ctx, &edge->id), ldb_strerror(ret)));
189 talloc_free(tmp_ctx);
190 return NT_STATUS_INTERNAL_DB_CORRUPTION;
192 if (res->count == 0) {
193 DEBUG(0, ("failed to find siteLink object %s\n",
194 GUID_string(tmp_ctx, &edge->id)));
196 talloc_free(tmp_ctx);
197 return NT_STATUS_INTERNAL_DB_CORRUPTION;
200 el = ldb_msg_find_element(res->msgs[0], "siteList");
201 edge->vertex_ids.count = el->num_values;
202 edge->vertex_ids.data = talloc_array(edge, struct GUID, el->num_values);
203 if (!edge->vertex_ids.data) {
205 talloc_free(tmp_ctx);
206 return NT_STATUS_NO_MEMORY;
209 for (i = 0; i < el->num_values; i++) {
214 dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &val);
215 ret = dsdb_find_guid_by_dn(ldb, dn, &guid);
216 if (ret != LDB_SUCCESS) {
217 DEBUG(0, ("failed to find objectGUID for object %s: "
218 "%s\n", ldb_dn_get_linearized(dn),
221 talloc_free(tmp_ctx);
222 return NT_STATUS_INTERNAL_DB_CORRUPTION;
225 edge->vertex_ids.data = talloc_realloc(edge,
226 edge->vertex_ids.data,
228 edge->vertex_ids.count + 1);
229 if (!edge->vertex_ids.data) {
231 talloc_free(tmp_ctx);
232 return NT_STATUS_NO_MEMORY;
234 edge->vertex_ids.data[edge->vertex_ids.count] = guid;
235 edge->vertex_ids.count++;
238 edge->repl_info.cost = samdb_result_int64(site_link, "cost", 0);
239 edge->repl_info.options = samdb_result_int64(site_link, "options", 0);
240 edge->repl_info.interval = samdb_result_int64(site_link, "replInterval",
242 /* val = ldb_msg_find_ldb_val(site_link, "schedule");
243 edge->repl_info.schedule = val->data; */
245 edge->directed = false;
248 talloc_free(tmp_ctx);
253 * create a kcctpl_multi_edge_set instance containing edges for all siteLink
256 static NTSTATUS kcctpl_create_auto_edge_set(struct kcctpl_graph *graph,
258 struct ldb_result *res_site_link,
259 struct kcctpl_multi_edge_set **_set)
261 struct kcctpl_multi_edge_set *set;
264 set = talloc_zero(graph, struct kcctpl_multi_edge_set);
265 NT_STATUS_HAVE_NO_MEMORY(set);
267 for (i = 0; i < res_site_link->count; i++) {
268 struct GUID site_link_guid;
269 struct kcctpl_multi_edge *edge;
271 site_link_guid = samdb_result_guid(res_site_link->msgs[i],
273 edge = kcctpl_find_edge_by_guid(graph, site_link_guid);
275 TALLOC_CTX *tmp_ctx = talloc_new(graph);
276 DEBUG(0, ("failed to find a graph edge with ID=%s\n",
277 GUID_string(tmp_ctx, &site_link_guid)));
278 talloc_free(tmp_ctx);
279 return NT_STATUS_INTERNAL_DB_CORRUPTION;
282 if (GUID_compare(&edge->type, &type) == 0) {
283 set->edge_ids.data = talloc_realloc(set,
286 set->edge_ids.count + 1);
287 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set->edge_ids.data,
289 set->edge_ids.data[set->edge_ids.count] = site_link_guid;
290 set->edge_ids.count++;
299 * create a kcctpl_multi_edge_set instance.
301 static NTSTATUS kcctpl_create_edge_set(struct ldb_context *ldb,
302 struct kcctpl_graph *graph,
304 struct ldb_message *bridge,
305 struct kcctpl_multi_edge_set **_set)
307 struct kcctpl_multi_edge_set *set;
308 struct ldb_message_element *el;
311 set = talloc_zero(graph, struct kcctpl_multi_edge_set);
312 NT_STATUS_HAVE_NO_MEMORY(set);
314 set->id = samdb_result_guid(bridge, "objectGUID");
316 el = ldb_msg_find_element(bridge, "siteLinkList");
317 for (i = 0; i < el->num_values; i++) {
321 struct GUID site_link_guid;
323 struct kcctpl_multi_edge *edge;
326 tmp_ctx = talloc_new(graph);
327 dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &val);
328 if (!ldb_dn_validate(dn)) {
329 DEBUG(0, ("invalid DN in siteLinkList attr of %s\n",
330 GUID_string(tmp_ctx, &set->id)));
332 talloc_free(tmp_ctx);
333 return NT_STATUS_INTERNAL_DB_CORRUPTION;
336 ret = dsdb_find_guid_by_dn(ldb, dn, &site_link_guid);
337 if (ret != LDB_SUCCESS) {
338 DEBUG(0, ("failed to find objectGUID for object %s: "
339 "%s\n", ldb_dn_get_linearized(dn),
342 talloc_free(tmp_ctx);
343 return NT_STATUS_INTERNAL_DB_CORRUPTION;
346 edge = kcctpl_find_edge_by_guid(graph, site_link_guid);
348 DEBUG(0, ("failed to find a graph edge with ID=%s\n",
349 GUID_string(tmp_ctx, &site_link_guid)));
351 talloc_free(tmp_ctx);
352 return NT_STATUS_INTERNAL_DB_CORRUPTION;
355 talloc_free(tmp_ctx);
357 if (GUID_compare(&edge->type, &type) == 0) {
358 set->edge_ids.data = talloc_realloc(set,
361 set->edge_ids.count + 1);
362 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set->edge_ids.data,
364 set->edge_ids.data[set->edge_ids.count] = site_link_guid;
365 set->edge_ids.count++;
374 * set up a kcctpl_graph, populated with a kcctpl_vertex for each site object, a
375 * kcctpl_multi_edge for each siteLink object, and a kcctpl_multi_edge_set for
376 * each siteLinkBridge object (or implied siteLinkBridge).
378 static NTSTATUS kcctpl_setup_graph(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
379 struct kcctpl_graph **_graph)
381 struct kcctpl_graph *graph;
382 struct ldb_dn *config_dn, *base_dn;
385 struct ldb_result *res;
386 const char * const site_link_attrs[] = { "objectGUID", NULL };
387 const char * const inter_site_transport_attrs[] = { "objectGUID",
390 const char * const attrs[] = { "objectGUID", "cost", "options",
391 "replInterval", "schedule", NULL };
392 const char * const site_link_bridge_attrs[] = { "objectGUID",
396 struct GUID_list vertex_ids;
400 config_dn = samdb_config_dn(ldb);
402 DEBUG(0, ("failed to find our own Config DN\n"));
403 return NT_STATUS_INTERNAL_DB_CORRUPTION;
406 tmp_ctx = talloc_new(mem_ctx);
407 base_dn = ldb_dn_copy(tmp_ctx, config_dn);
409 DEBUG(0, ("failed to copy Config DN %s\n",
410 ldb_dn_get_linearized(config_dn)));
411 talloc_free(tmp_ctx);
412 return NT_STATUS_NO_MEMORY;
415 ok = ldb_dn_add_child_fmt(base_dn, "CN=Sites");
417 if (ldb_dn_validate(base_dn)) {
418 DEBUG(0, ("failed to format DN %s\n",
419 ldb_dn_get_linearized(base_dn)));
421 talloc_free(tmp_ctx);
422 return NT_STATUS_NO_MEMORY;
425 ret = ldb_search(ldb, tmp_ctx, &res, base_dn, LDB_SCOPE_SUBTREE,
426 site_link_attrs, "objectClass=siteLink");
427 if (ret != LDB_SUCCESS) {
428 DEBUG(0, ("failed to find siteLink objects under %s: %s\n",
429 ldb_dn_get_linearized(base_dn), ldb_strerror(ret)));
430 talloc_free(tmp_ctx);
431 return NT_STATUS_INTERNAL_DB_CORRUPTION;
434 ZERO_STRUCT(vertex_ids);
435 for (i = 0; i < res->count; i++) {
438 guid = samdb_result_guid(res->msgs[i], "objectGUID");
439 vertex_ids.data = talloc_realloc(tmp_ctx, vertex_ids.data,
441 vertex_ids.count + 1);
442 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(vertex_ids.data, tmp_ctx);
443 vertex_ids.data[vertex_ids.count] = guid;
447 status = kcctpl_create_graph(mem_ctx, &vertex_ids, &graph);
448 if (NT_STATUS_IS_ERR(status)) {
449 DEBUG(0, ("failed to create graph: %s\n", nt_errstr(status)));
450 talloc_free(tmp_ctx);
454 /* get site of local DC */
456 ok = ldb_dn_add_child_fmt(base_dn, "CN=Inter-Site Transports");
458 if (ldb_dn_validate(base_dn)) {
459 DEBUG(0, ("failed to format DN %s\n",
460 ldb_dn_get_linearized(base_dn)));
462 talloc_free(tmp_ctx);
463 return NT_STATUS_NO_MEMORY;
466 ret = ldb_search(ldb, tmp_ctx, &res, base_dn, LDB_SCOPE_SUBTREE,
467 inter_site_transport_attrs,
468 "objectClass=interSiteTransport");
469 if (ret != LDB_SUCCESS) {
470 DEBUG(0, ("failed to find interSiteTransport objects under %s: "
471 "%s\n", ldb_dn_get_linearized(base_dn),
473 talloc_free(tmp_ctx);
474 return NT_STATUS_INTERNAL_DB_CORRUPTION;
477 for (i = 0; i < res->count; i++) {
478 struct ldb_message *transport;
479 struct ldb_result *res_site_link;
480 struct GUID transport_guid;
484 transport = res->msgs[i];
486 base_dn = samdb_result_dn(ldb, tmp_ctx, transport,
487 "distinguishedName", NULL);
489 DEBUG(0, ("failed to find DN for interSiteTransport "
492 talloc_free(tmp_ctx);
493 return NT_STATUS_INTERNAL_DB_CORRUPTION;
496 /* TODO: don't need to ldb_search again; search in res. */
497 ret = ldb_search(ldb, tmp_ctx, &res_site_link, base_dn,
498 LDB_SCOPE_SUBTREE, attrs,
499 "objectClass=siteLink");
500 if (ret != LDB_SUCCESS) {
501 DEBUG(0, ("failed to find siteLink objects under %s: "
502 "%s\n", ldb_dn_get_linearized(base_dn),
505 talloc_free(tmp_ctx);
506 return NT_STATUS_INTERNAL_DB_CORRUPTION;
509 transport_guid = samdb_result_guid(transport, "objectGUID");
510 for (j = 0; j < res_site_link->count; j++) {
511 struct kcctpl_multi_edge *edge;
513 status = kcctpl_create_edge(ldb, graph, transport_guid,
514 res_site_link->msgs[j],
516 if (NT_STATUS_IS_ERR(status)) {
517 DEBUG(0, ("failed to create edge: %s\n",
520 talloc_free(tmp_ctx);
524 graph->edges.data = talloc_realloc(graph,
526 struct kcctpl_multi_edge,
527 graph->edges.count + 1);
528 if (!graph->edges.data) {
530 talloc_free(tmp_ctx);
531 return NT_STATUS_NO_MEMORY;
533 graph->edges.data[graph->edges.count] = *edge;
534 graph->edges.count++;
537 options = samdb_result_int64(transport, "options", 0);
538 if ((options & NTDSTRANSPORT_OPT_BRIDGES_REQUIRED) == 0) {
539 struct kcctpl_multi_edge_set *edge_set;
541 status = kcctpl_create_auto_edge_set(graph,
545 if (NT_STATUS_IS_ERR(status)) {
546 DEBUG(0, ("failed to create edge set: %s\n",
549 talloc_free(tmp_ctx);
553 graph->edge_sets.data = talloc_realloc(graph,
554 graph->edge_sets.data,
555 struct kcctpl_multi_edge_set,
556 graph->edge_sets.count + 1);
557 if (!graph->edge_sets.data) {
559 talloc_free(tmp_ctx);
560 return NT_STATUS_NO_MEMORY;
562 graph->edge_sets.data[graph->edge_sets.count] = *edge_set;
563 graph->edge_sets.count++;
565 ret = ldb_search(ldb, tmp_ctx, &res_site_link, base_dn,
567 site_link_bridge_attrs,
568 "objectClass=siteLinkBridge");
569 if (ret != LDB_SUCCESS) {
570 DEBUG(0, ("failed to find siteLinkBridge "
571 "objects under %s: %s\n",
572 ldb_dn_get_linearized(base_dn),
575 talloc_free(tmp_ctx);
576 return NT_STATUS_INTERNAL_DB_CORRUPTION;
579 for (j = 0; j < res_site_link->count; j++) {
580 struct ldb_message *bridge;
581 struct kcctpl_multi_edge_set *edge_set;
583 bridge = res_site_link->msgs[j];
584 status = kcctpl_create_edge_set(ldb, graph,
588 if (NT_STATUS_IS_ERR(status)) {
589 DEBUG(0, ("failed to create edge set: "
590 "%s\n", nt_errstr(status)));
592 talloc_free(tmp_ctx);
596 graph->edge_sets.data = talloc_realloc(graph,
597 graph->edge_sets.data,
598 struct kcctpl_multi_edge_set,
599 graph->edge_sets.count + 1);
600 if (!graph->edge_sets.data) {
602 talloc_free(tmp_ctx);
603 return NT_STATUS_NO_MEMORY;
605 graph->edge_sets.data[graph->edge_sets.count] = *edge_set;
606 graph->edge_sets.count++;
612 talloc_free(tmp_ctx);