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 TYPESAFE_QSORT(guids
->data
, guids
->count
, GUID_compare
);
151 for (i
= 0; i
< guids
->count
; i
++) {
152 graph
->vertices
.data
[i
].id
= guids
->data
[i
];
160 * create a kcctpl_multi_edge instance.
162 static NTSTATUS
kcctpl_create_edge(struct ldb_context
*ldb
, TALLOC_CTX
*mem_ctx
,
164 struct ldb_message
*site_link
,
165 struct kcctpl_multi_edge
**_edge
)
167 struct kcctpl_multi_edge
*edge
;
169 struct ldb_result
*res
;
170 const char * const attrs
[] = { "siteList", NULL
};
172 struct ldb_message_element
*el
;
176 edge
= talloc(mem_ctx
, struct kcctpl_multi_edge
);
177 NT_STATUS_HAVE_NO_MEMORY(edge
);
179 edge
->id
= samdb_result_guid(site_link
, "objectGUID");
181 tmp_ctx
= talloc_new(mem_ctx
);
182 ret
= ldb_search(ldb
, tmp_ctx
, &res
, NULL
, LDB_SCOPE_BASE
, attrs
,
183 "objectGUID=%s", GUID_string(tmp_ctx
, &edge
->id
));
184 if (ret
!= LDB_SUCCESS
) {
185 DEBUG(0, ("failed to find siteLink object %s: %s\n",
186 GUID_string(tmp_ctx
, &edge
->id
), ldb_strerror(ret
)));
188 talloc_free(tmp_ctx
);
189 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
191 if (res
->count
== 0) {
192 DEBUG(0, ("failed to find siteLink object %s\n",
193 GUID_string(tmp_ctx
, &edge
->id
)));
195 talloc_free(tmp_ctx
);
196 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
199 el
= ldb_msg_find_element(res
->msgs
[0], "siteList");
200 edge
->vertex_ids
.count
= el
->num_values
;
201 edge
->vertex_ids
.data
= talloc_array(edge
, struct GUID
, el
->num_values
);
202 if (!edge
->vertex_ids
.data
) {
204 talloc_free(tmp_ctx
);
205 return NT_STATUS_NO_MEMORY
;
208 for (i
= 0; i
< el
->num_values
; i
++) {
213 dn
= ldb_dn_from_ldb_val(tmp_ctx
, ldb
, &val
);
214 ret
= dsdb_find_guid_by_dn(ldb
, dn
, &guid
);
215 if (ret
!= LDB_SUCCESS
) {
216 DEBUG(0, ("failed to find objectGUID for object %s: "
217 "%s\n", ldb_dn_get_linearized(dn
),
220 talloc_free(tmp_ctx
);
221 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
224 edge
->vertex_ids
.data
= talloc_realloc(edge
,
225 edge
->vertex_ids
.data
,
227 edge
->vertex_ids
.count
+ 1);
228 if (!edge
->vertex_ids
.data
) {
230 talloc_free(tmp_ctx
);
231 return NT_STATUS_NO_MEMORY
;
233 edge
->vertex_ids
.data
[edge
->vertex_ids
.count
] = guid
;
234 edge
->vertex_ids
.count
++;
237 edge
->repl_info
.cost
= samdb_result_int64(site_link
, "cost", 0);
238 edge
->repl_info
.options
= samdb_result_int64(site_link
, "options", 0);
239 edge
->repl_info
.interval
= samdb_result_int64(site_link
, "replInterval",
241 /* val = ldb_msg_find_ldb_val(site_link, "schedule");
242 edge->repl_info.schedule = val->data; */
244 edge
->directed
= false;
247 talloc_free(tmp_ctx
);
252 * create a kcctpl_multi_edge_set instance containing edges for all siteLink
255 static NTSTATUS
kcctpl_create_auto_edge_set(struct kcctpl_graph
*graph
,
257 struct ldb_result
*res_site_link
,
258 struct kcctpl_multi_edge_set
**_set
)
260 struct kcctpl_multi_edge_set
*set
;
263 set
= talloc_zero(graph
, struct kcctpl_multi_edge_set
);
264 NT_STATUS_HAVE_NO_MEMORY(set
);
266 for (i
= 0; i
< res_site_link
->count
; i
++) {
267 struct GUID site_link_guid
;
268 struct kcctpl_multi_edge
*edge
;
270 site_link_guid
= samdb_result_guid(res_site_link
->msgs
[i
],
272 edge
= kcctpl_find_edge_by_guid(graph
, site_link_guid
);
274 TALLOC_CTX
*tmp_ctx
= talloc_new(graph
);
275 DEBUG(0, ("failed to find a graph edge with ID=%s\n",
276 GUID_string(tmp_ctx
, &site_link_guid
)));
277 talloc_free(tmp_ctx
);
278 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
281 if (GUID_compare(&edge
->type
, &type
) == 0) {
282 set
->edge_ids
.data
= talloc_realloc(set
,
285 set
->edge_ids
.count
+ 1);
286 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set
->edge_ids
.data
,
288 set
->edge_ids
.data
[set
->edge_ids
.count
] = site_link_guid
;
289 set
->edge_ids
.count
++;
298 * create a kcctpl_multi_edge_set instance.
300 static NTSTATUS
kcctpl_create_edge_set(struct ldb_context
*ldb
,
301 struct kcctpl_graph
*graph
,
303 struct ldb_message
*bridge
,
304 struct kcctpl_multi_edge_set
**_set
)
306 struct kcctpl_multi_edge_set
*set
;
307 struct ldb_message_element
*el
;
310 set
= talloc_zero(graph
, struct kcctpl_multi_edge_set
);
311 NT_STATUS_HAVE_NO_MEMORY(set
);
313 set
->id
= samdb_result_guid(bridge
, "objectGUID");
315 el
= ldb_msg_find_element(bridge
, "siteLinkList");
316 for (i
= 0; i
< el
->num_values
; i
++) {
320 struct GUID site_link_guid
;
322 struct kcctpl_multi_edge
*edge
;
325 tmp_ctx
= talloc_new(graph
);
326 dn
= ldb_dn_from_ldb_val(tmp_ctx
, ldb
, &val
);
327 if (!ldb_dn_validate(dn
)) {
328 DEBUG(0, ("invalid DN in siteLinkList attr of %s\n",
329 GUID_string(tmp_ctx
, &set
->id
)));
331 talloc_free(tmp_ctx
);
332 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
335 ret
= dsdb_find_guid_by_dn(ldb
, dn
, &site_link_guid
);
336 if (ret
!= LDB_SUCCESS
) {
337 DEBUG(0, ("failed to find objectGUID for object %s: "
338 "%s\n", ldb_dn_get_linearized(dn
),
341 talloc_free(tmp_ctx
);
342 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
345 edge
= kcctpl_find_edge_by_guid(graph
, site_link_guid
);
347 DEBUG(0, ("failed to find a graph edge with ID=%s\n",
348 GUID_string(tmp_ctx
, &site_link_guid
)));
350 talloc_free(tmp_ctx
);
351 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
354 talloc_free(tmp_ctx
);
356 if (GUID_compare(&edge
->type
, &type
) == 0) {
357 set
->edge_ids
.data
= talloc_realloc(set
,
360 set
->edge_ids
.count
+ 1);
361 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set
->edge_ids
.data
,
363 set
->edge_ids
.data
[set
->edge_ids
.count
] = site_link_guid
;
364 set
->edge_ids
.count
++;
373 * set up a kcctpl_graph, populated with a kcctpl_vertex for each site object, a
374 * kcctpl_multi_edge for each siteLink object, and a kcctpl_multi_edge_set for
375 * each siteLinkBridge object (or implied siteLinkBridge).
377 static NTSTATUS
kcctpl_setup_graph(struct ldb_context
*ldb
, TALLOC_CTX
*mem_ctx
,
378 struct kcctpl_graph
**_graph
)
380 struct kcctpl_graph
*graph
;
381 struct ldb_dn
*config_dn
, *base_dn
;
384 struct ldb_result
*res
;
385 const char * const site_link_attrs
[] = { "objectGUID", NULL
};
386 const char * const inter_site_transport_attrs
[] = { "objectGUID",
389 const char * const attrs
[] = { "objectGUID", "cost", "options",
390 "replInterval", "schedule", NULL
};
391 const char * const site_link_bridge_attrs
[] = { "objectGUID",
395 struct GUID_list vertex_ids
;
399 config_dn
= samdb_config_dn(ldb
);
401 DEBUG(0, ("failed to find our own Config DN\n"));
402 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
405 tmp_ctx
= talloc_new(mem_ctx
);
406 base_dn
= ldb_dn_copy(tmp_ctx
, config_dn
);
408 DEBUG(0, ("failed to copy Config DN %s\n",
409 ldb_dn_get_linearized(config_dn
)));
410 talloc_free(tmp_ctx
);
411 return NT_STATUS_NO_MEMORY
;
414 ok
= ldb_dn_add_child_fmt(base_dn
, "CN=Sites");
416 if (ldb_dn_validate(base_dn
)) {
417 DEBUG(0, ("failed to format DN %s\n",
418 ldb_dn_get_linearized(base_dn
)));
420 talloc_free(tmp_ctx
);
421 return NT_STATUS_NO_MEMORY
;
424 ret
= ldb_search(ldb
, tmp_ctx
, &res
, base_dn
, LDB_SCOPE_SUBTREE
,
425 site_link_attrs
, "objectClass=siteLink");
426 if (ret
!= LDB_SUCCESS
) {
427 DEBUG(0, ("failed to find siteLink objects under %s: %s\n",
428 ldb_dn_get_linearized(base_dn
), ldb_strerror(ret
)));
429 talloc_free(tmp_ctx
);
430 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
433 ZERO_STRUCT(vertex_ids
);
434 for (i
= 0; i
< res
->count
; i
++) {
437 guid
= samdb_result_guid(res
->msgs
[i
], "objectGUID");
438 vertex_ids
.data
= talloc_realloc(tmp_ctx
, vertex_ids
.data
,
440 vertex_ids
.count
+ 1);
441 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(vertex_ids
.data
, tmp_ctx
);
442 vertex_ids
.data
[vertex_ids
.count
] = guid
;
446 status
= kcctpl_create_graph(mem_ctx
, &vertex_ids
, &graph
);
447 if (NT_STATUS_IS_ERR(status
)) {
448 DEBUG(0, ("failed to create graph: %s\n", nt_errstr(status
)));
449 talloc_free(tmp_ctx
);
453 /* get site of local DC */
455 ok
= ldb_dn_add_child_fmt(base_dn
, "CN=Inter-Site Transports");
457 if (ldb_dn_validate(base_dn
)) {
458 DEBUG(0, ("failed to format DN %s\n",
459 ldb_dn_get_linearized(base_dn
)));
461 talloc_free(tmp_ctx
);
462 return NT_STATUS_NO_MEMORY
;
465 ret
= ldb_search(ldb
, tmp_ctx
, &res
, base_dn
, LDB_SCOPE_SUBTREE
,
466 inter_site_transport_attrs
,
467 "objectClass=interSiteTransport");
468 if (ret
!= LDB_SUCCESS
) {
469 DEBUG(0, ("failed to find interSiteTransport objects under %s: "
470 "%s\n", ldb_dn_get_linearized(base_dn
),
472 talloc_free(tmp_ctx
);
473 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
476 for (i
= 0; i
< res
->count
; i
++) {
477 struct ldb_message
*transport
;
478 struct ldb_result
*res_site_link
;
479 struct GUID transport_guid
;
483 transport
= res
->msgs
[i
];
485 base_dn
= samdb_result_dn(ldb
, tmp_ctx
, transport
,
486 "distinguishedName", NULL
);
488 DEBUG(0, ("failed to find DN for interSiteTransport "
491 talloc_free(tmp_ctx
);
492 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
495 /* TODO: don't need to ldb_search again; search in res. */
496 ret
= ldb_search(ldb
, tmp_ctx
, &res_site_link
, base_dn
,
497 LDB_SCOPE_SUBTREE
, attrs
,
498 "objectClass=siteLink");
499 if (ret
!= LDB_SUCCESS
) {
500 DEBUG(0, ("failed to find siteLink objects under %s: "
501 "%s\n", ldb_dn_get_linearized(base_dn
),
504 talloc_free(tmp_ctx
);
505 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
508 transport_guid
= samdb_result_guid(transport
, "objectGUID");
509 for (j
= 0; j
< res_site_link
->count
; j
++) {
510 struct kcctpl_multi_edge
*edge
;
512 status
= kcctpl_create_edge(ldb
, graph
, transport_guid
,
513 res_site_link
->msgs
[j
],
515 if (NT_STATUS_IS_ERR(status
)) {
516 DEBUG(0, ("failed to create edge: %s\n",
519 talloc_free(tmp_ctx
);
523 graph
->edges
.data
= talloc_realloc(graph
,
525 struct kcctpl_multi_edge
,
526 graph
->edges
.count
+ 1);
527 if (!graph
->edges
.data
) {
529 talloc_free(tmp_ctx
);
530 return NT_STATUS_NO_MEMORY
;
532 graph
->edges
.data
[graph
->edges
.count
] = *edge
;
533 graph
->edges
.count
++;
536 options
= samdb_result_int64(transport
, "options", 0);
537 if ((options
& NTDSTRANSPORT_OPT_BRIDGES_REQUIRED
) == 0) {
538 struct kcctpl_multi_edge_set
*edge_set
;
540 status
= kcctpl_create_auto_edge_set(graph
,
544 if (NT_STATUS_IS_ERR(status
)) {
545 DEBUG(0, ("failed to create edge set: %s\n",
548 talloc_free(tmp_ctx
);
552 graph
->edge_sets
.data
= talloc_realloc(graph
,
553 graph
->edge_sets
.data
,
554 struct kcctpl_multi_edge_set
,
555 graph
->edge_sets
.count
+ 1);
556 if (!graph
->edge_sets
.data
) {
558 talloc_free(tmp_ctx
);
559 return NT_STATUS_NO_MEMORY
;
561 graph
->edge_sets
.data
[graph
->edge_sets
.count
] = *edge_set
;
562 graph
->edge_sets
.count
++;
564 ret
= ldb_search(ldb
, tmp_ctx
, &res_site_link
, base_dn
,
566 site_link_bridge_attrs
,
567 "objectClass=siteLinkBridge");
568 if (ret
!= LDB_SUCCESS
) {
569 DEBUG(0, ("failed to find siteLinkBridge "
570 "objects under %s: %s\n",
571 ldb_dn_get_linearized(base_dn
),
574 talloc_free(tmp_ctx
);
575 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
578 for (j
= 0; j
< res_site_link
->count
; j
++) {
579 struct ldb_message
*bridge
;
580 struct kcctpl_multi_edge_set
*edge_set
;
582 bridge
= res_site_link
->msgs
[j
];
583 status
= kcctpl_create_edge_set(ldb
, graph
,
587 if (NT_STATUS_IS_ERR(status
)) {
588 DEBUG(0, ("failed to create edge set: "
589 "%s\n", nt_errstr(status
)));
591 talloc_free(tmp_ctx
);
595 graph
->edge_sets
.data
= talloc_realloc(graph
,
596 graph
->edge_sets
.data
,
597 struct kcctpl_multi_edge_set
,
598 graph
->edge_sets
.count
+ 1);
599 if (!graph
->edge_sets
.data
) {
601 talloc_free(tmp_ctx
);
602 return NT_STATUS_NO_MEMORY
;
604 graph
->edge_sets
.data
[graph
->edge_sets
.count
] = *edge_set
;
605 graph
->edge_sets
.count
++;
611 talloc_free(tmp_ctx
);