s4-kcc: remove a qsort() that snuck into the new topology code
[abartlet/samba.git/.git] / source4 / dsdb / kcc / kcc_topology.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    KCC service
5
6    Copyright (C) Crístian Deives 2010
7
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.
12
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.
17
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/>.
20
21 */
22
23 #include "includes.h"
24 #include "dsdb/samdb/samdb.h"
25 #include "lib/messaging/irpc.h"
26 #include "librpc/gen_ndr/ndr_misc.h"
27
28 #define NTDSTRANSPORT_OPT_IGNORE_SCHEDULES 0x00000001
29 #define NTDSTRANSPORT_OPT_BRIDGES_REQUIRED 0x00000002
30
31 /** replication parameters of a graph edge */
32 struct kcctpl_repl_info {
33         int cost;
34         int interval;
35         int options;
36         uint8_t schedule[84];
37 };
38
39 /** color of a vertex */
40 enum kcctpl_color { RED, BLACK, WHITE };
41
42 /** a GUID array list */
43 struct GUID_list {
44         struct GUID *data;
45         unsigned int count;
46 };
47
48 /** a vertex in the site graph */
49 struct kcctpl_vertex {
50         struct GUID id;
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;
56         int dist_to_red;
57
58         /* Dijkstra data */
59         struct GUID root_id;
60         bool demoted;
61
62         /* Kruskal data */
63         struct GUID component_id;
64         int component_index;
65 };
66
67 /** fully connected subgraph of vertices */
68 struct kcctpl_multi_edge {
69         struct GUID id;
70         struct GUID_list vertex_ids;
71         struct GUID type;
72         struct kcctpl_repl_info repl_info;
73         bool directed;
74 };
75
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 {
79         struct GUID id;
80         struct GUID_list edge_ids;
81 };
82
83 /** a vertices array list */
84 struct kcctpl_vertex_list {
85         struct kcctpl_vertex *data;
86         unsigned int count;
87 };
88
89 /** an edges linked list */
90 struct kcctpl_multi_edge_list {
91         struct kcctpl_multi_edge *data;
92         unsigned int count;
93 };
94
95 /** an edge sets linked list */
96 struct kcctpl_multi_edge_set_list {
97         struct kcctpl_multi_edge_set *data;
98         unsigned int count;
99 };
100
101 /** a site graph */
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;
106 };
107
108 /** path found in the graph between two non-white vertices */
109 struct kcctpl_internal_edge {
110         struct GUID v1id, v2id;
111         bool red_red;
112         struct kcctpl_repl_info repl_info;
113         struct GUID type;
114 };
115
116 /**
117  * find a graph edge based on its GUID.
118  */
119 static struct kcctpl_multi_edge *kcctpl_find_edge_by_guid(struct kcctpl_graph *graph,
120                                                           struct GUID guid)
121 {
122         unsigned int i;
123
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];
127                 }
128         }
129         return NULL;
130 }
131 /**
132  * create a kcctpl_graph instance.
133  */
134 static NTSTATUS kcctpl_create_graph(TALLOC_CTX *mem_ctx,
135                                     struct GUID_list *guids,
136                                     struct kcctpl_graph **_graph)
137 {
138         struct kcctpl_graph *graph;
139         unsigned int i;
140
141         graph = talloc_zero(mem_ctx, struct kcctpl_graph);
142         NT_STATUS_HAVE_NO_MEMORY(graph);
143
144         graph->vertices.count = guids->count;
145         graph->vertices.data = talloc_zero_array(graph, struct kcctpl_vertex,
146                                                  guids->count);
147         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(graph->vertices.data, graph);
148
149         TYPESAFE_QSORT(guids->data, guids->count, GUID_compare);
150
151         for (i = 0; i < guids->count; i++) {
152                 graph->vertices.data[i].id = guids->data[i];
153         }
154
155         *_graph = graph;
156         return NT_STATUS_OK;
157 }
158
159 /**
160  * create a kcctpl_multi_edge instance.
161  */
162 static NTSTATUS kcctpl_create_edge(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
163                                    struct GUID type,
164                                    struct ldb_message *site_link,
165                                    struct kcctpl_multi_edge **_edge)
166 {
167         struct kcctpl_multi_edge *edge;
168         TALLOC_CTX *tmp_ctx;
169         struct ldb_result *res;
170         const char * const attrs[] = { "siteList", NULL };
171         int ret;
172         struct ldb_message_element *el;
173         unsigned int i;
174         struct ldb_val val;
175
176         edge = talloc(mem_ctx, struct kcctpl_multi_edge);
177         NT_STATUS_HAVE_NO_MEMORY(edge);
178
179         edge->id = samdb_result_guid(site_link, "objectGUID");
180
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)));
187                 talloc_free(edge);
188                 talloc_free(tmp_ctx);
189                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
190         }
191         if (res->count == 0) {
192                 DEBUG(0, ("failed to find siteLink object %s\n",
193                           GUID_string(tmp_ctx, &edge->id)));
194                 talloc_free(edge);
195                 talloc_free(tmp_ctx);
196                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
197         }
198
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) {
203                 talloc_free(edge);
204                 talloc_free(tmp_ctx);
205                 return NT_STATUS_NO_MEMORY;
206         }
207
208         for (i = 0; i < el->num_values; i++) {
209                 struct ldb_dn *dn;
210                 struct GUID guid;
211
212                 val = el->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),
218                                   ldb_strerror(ret)));
219                         talloc_free(edge);
220                         talloc_free(tmp_ctx);
221                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
222                 }
223
224                 edge->vertex_ids.data = talloc_realloc(edge,
225                                                        edge->vertex_ids.data,
226                                                        struct GUID,
227                                                        edge->vertex_ids.count + 1);
228                 if (!edge->vertex_ids.data) {
229                         talloc_free(edge);
230                         talloc_free(tmp_ctx);
231                         return NT_STATUS_NO_MEMORY;
232                 }
233                 edge->vertex_ids.data[edge->vertex_ids.count] = guid;
234                 edge->vertex_ids.count++;
235         }
236
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",
240                                                       0);
241         /* val = ldb_msg_find_ldb_val(site_link, "schedule");
242         edge->repl_info.schedule = val->data; */
243         edge->type = type;
244         edge->directed = false;
245
246         *_edge = edge;
247         talloc_free(tmp_ctx);
248         return NT_STATUS_OK;
249 }
250
251 /**
252  * create a kcctpl_multi_edge_set instance containing edges for all siteLink
253  * objects.
254  */
255 static NTSTATUS kcctpl_create_auto_edge_set(struct kcctpl_graph *graph,
256                                             struct GUID type,
257                                             struct ldb_result *res_site_link,
258                                             struct kcctpl_multi_edge_set **_set)
259 {
260         struct kcctpl_multi_edge_set *set;
261         unsigned int i;
262
263         set = talloc_zero(graph, struct kcctpl_multi_edge_set);
264         NT_STATUS_HAVE_NO_MEMORY(set);
265
266         for (i = 0; i < res_site_link->count; i++) {
267                 struct GUID site_link_guid;
268                 struct kcctpl_multi_edge *edge;
269
270                 site_link_guid = samdb_result_guid(res_site_link->msgs[i],
271                                                    "objectGUID");
272                 edge = kcctpl_find_edge_by_guid(graph, site_link_guid);
273                 if (!edge) {
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;
279                 }
280
281                 if (GUID_compare(&edge->type, &type) == 0) {
282                         set->edge_ids.data = talloc_realloc(set,
283                                                             set->edge_ids.data,
284                                                             struct GUID,
285                                                             set->edge_ids.count + 1);
286                         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set->edge_ids.data,
287                                                           set);
288                         set->edge_ids.data[set->edge_ids.count] = site_link_guid;
289                         set->edge_ids.count++;
290                 }
291         }
292
293         *_set = set;
294         return NT_STATUS_OK;
295 }
296
297 /**
298  * create a kcctpl_multi_edge_set instance.
299  */
300 static NTSTATUS kcctpl_create_edge_set(struct ldb_context *ldb,
301                                        struct kcctpl_graph *graph,
302                                        struct GUID type,
303                                        struct ldb_message *bridge,
304                                        struct kcctpl_multi_edge_set **_set)
305 {
306         struct kcctpl_multi_edge_set *set;
307         struct ldb_message_element *el;
308         unsigned int i;
309
310         set = talloc_zero(graph, struct kcctpl_multi_edge_set);
311         NT_STATUS_HAVE_NO_MEMORY(set);
312
313         set->id = samdb_result_guid(bridge, "objectGUID");
314
315         el = ldb_msg_find_element(bridge, "siteLinkList");
316         for (i = 0; i < el->num_values; i++) {
317                 struct ldb_val val;
318                 TALLOC_CTX *tmp_ctx;
319                 struct ldb_dn *dn;
320                 struct GUID site_link_guid;
321                 int ret;
322                 struct kcctpl_multi_edge *edge;
323
324                 val = el->values[i];
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)));
330                         talloc_free(set);
331                         talloc_free(tmp_ctx);
332                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
333                 }
334
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),
339                                   ldb_strerror(ret)));
340                         talloc_free(set);
341                         talloc_free(tmp_ctx);
342                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
343                 }
344
345                 edge = kcctpl_find_edge_by_guid(graph, site_link_guid);
346                 if (!edge) {
347                         DEBUG(0, ("failed to find a graph edge with ID=%s\n",
348                                   GUID_string(tmp_ctx, &site_link_guid)));
349                         talloc_free(set);
350                         talloc_free(tmp_ctx);
351                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
352                 }
353
354                 talloc_free(tmp_ctx);
355
356                 if (GUID_compare(&edge->type, &type) == 0) {
357                         set->edge_ids.data = talloc_realloc(set,
358                                                             set->edge_ids.data,
359                                                             struct GUID,
360                                                             set->edge_ids.count + 1);
361                         NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set->edge_ids.data,
362                                                           set);
363                         set->edge_ids.data[set->edge_ids.count] = site_link_guid;
364                         set->edge_ids.count++;
365                 }
366         }
367
368         *_set = set;
369         return NT_STATUS_OK;
370 }
371
372 /**
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).
376  */
377 static NTSTATUS kcctpl_setup_graph(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
378                                    struct kcctpl_graph **_graph)
379 {
380         struct kcctpl_graph *graph;
381         struct ldb_dn *config_dn, *base_dn;
382         TALLOC_CTX *tmp_ctx;
383         bool ok;
384         struct ldb_result *res;
385         const char * const site_link_attrs[] = { "objectGUID", NULL };
386         const char * const inter_site_transport_attrs[] = { "objectGUID",
387                                                             "distinguishedName",
388                                                             NULL };
389         const char * const attrs[] = { "objectGUID", "cost", "options",
390                                        "replInterval", "schedule", NULL };
391         const char * const site_link_bridge_attrs[] = { "objectGUID",
392                                                         "siteLinkList",
393                                                         NULL };
394         int ret;
395         struct GUID_list vertex_ids;
396         unsigned int i;
397         NTSTATUS status;
398
399         config_dn = samdb_config_dn(ldb);
400         if (!config_dn) {
401                 DEBUG(0, ("failed to find our own Config DN\n"));
402                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
403         }
404
405         tmp_ctx = talloc_new(mem_ctx);
406         base_dn = ldb_dn_copy(tmp_ctx, config_dn);
407         if (!base_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;
412         }
413
414         ok = ldb_dn_add_child_fmt(base_dn, "CN=Sites");
415         if (!ok) {
416                 if (ldb_dn_validate(base_dn)) {
417                         DEBUG(0, ("failed to format DN %s\n",
418                                   ldb_dn_get_linearized(base_dn)));
419                 }
420                 talloc_free(tmp_ctx);
421                 return NT_STATUS_NO_MEMORY;
422         }
423
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;
431         }
432
433         ZERO_STRUCT(vertex_ids);
434         for (i = 0; i < res->count; i++) {
435                 struct GUID guid;
436
437                 guid = samdb_result_guid(res->msgs[i], "objectGUID");
438                 vertex_ids.data = talloc_realloc(tmp_ctx, vertex_ids.data,
439                                                  struct GUID,
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;
443                 vertex_ids.count++;
444         }
445
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);
450                 return status;
451         }
452
453         /* get site of local DC */
454
455         ok = ldb_dn_add_child_fmt(base_dn, "CN=Inter-Site Transports");
456         if (!ok) {
457                 if (ldb_dn_validate(base_dn)) {
458                         DEBUG(0, ("failed to format DN %s\n",
459                                   ldb_dn_get_linearized(base_dn)));
460                 }
461                 talloc_free(tmp_ctx);
462                 return NT_STATUS_NO_MEMORY;
463         }
464
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),
471                           ldb_strerror(ret)));
472                 talloc_free(tmp_ctx);
473                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
474         }
475
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;
480                 unsigned int j;
481                 int options;
482
483                 transport = res->msgs[i];
484
485                 base_dn = samdb_result_dn(ldb, tmp_ctx, transport,
486                                           "distinguishedName", NULL);
487                 if (!base_dn) {
488                         DEBUG(0, ("failed to find DN for interSiteTransport "
489                                   "object\n"));
490                         talloc_free(graph);
491                         talloc_free(tmp_ctx);
492                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
493                 }
494
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),
502                                   ldb_strerror(ret)));
503                         talloc_free(graph);
504                         talloc_free(tmp_ctx);
505                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
506                 }
507
508                 transport_guid = samdb_result_guid(transport, "objectGUID");
509                 for (j = 0; j < res_site_link->count; j++) {
510                         struct kcctpl_multi_edge *edge;
511
512                         status = kcctpl_create_edge(ldb, graph, transport_guid,
513                                                     res_site_link->msgs[j],
514                                                     &edge);
515                         if (NT_STATUS_IS_ERR(status)) {
516                                 DEBUG(0, ("failed to create edge: %s\n",
517                                           nt_errstr(status)));
518                                 talloc_free(graph);
519                                 talloc_free(tmp_ctx);
520                                 return status;
521                         }
522
523                         graph->edges.data = talloc_realloc(graph,
524                                                            graph->edges.data,
525                                                            struct kcctpl_multi_edge,
526                                                            graph->edges.count + 1);
527                         if (!graph->edges.data) {
528                                 talloc_free(graph);
529                                 talloc_free(tmp_ctx);
530                                 return NT_STATUS_NO_MEMORY;
531                         }
532                         graph->edges.data[graph->edges.count] = *edge;
533                         graph->edges.count++;
534                 }
535
536                 options = samdb_result_int64(transport, "options", 0);
537                 if ((options & NTDSTRANSPORT_OPT_BRIDGES_REQUIRED) == 0) {
538                         struct kcctpl_multi_edge_set *edge_set;
539
540                         status = kcctpl_create_auto_edge_set(graph,
541                                                              transport_guid,
542                                                              res_site_link,
543                                                              &edge_set);
544                         if (NT_STATUS_IS_ERR(status)) {
545                                 DEBUG(0, ("failed to create edge set: %s\n",
546                                           nt_errstr(status)));
547                                 talloc_free(graph);
548                                 talloc_free(tmp_ctx);
549                                 return status;
550                         }
551
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) {
557                                 talloc_free(graph);
558                                 talloc_free(tmp_ctx);
559                                 return NT_STATUS_NO_MEMORY;
560                         }
561                         graph->edge_sets.data[graph->edge_sets.count] = *edge_set;
562                         graph->edge_sets.count++;
563                 } else {
564                         ret = ldb_search(ldb, tmp_ctx, &res_site_link, base_dn,
565                                          LDB_SCOPE_SUBTREE,
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),
572                                           ldb_strerror(ret)));
573                                 talloc_free(graph);
574                                 talloc_free(tmp_ctx);
575                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
576                         }
577
578                         for (j = 0; j < res_site_link->count; j++) {
579                                 struct ldb_message *bridge;
580                                 struct kcctpl_multi_edge_set *edge_set;
581
582                                 bridge = res_site_link->msgs[j];
583                                 status = kcctpl_create_edge_set(ldb, graph,
584                                                                 transport_guid,
585                                                                 bridge,
586                                                                 &edge_set);
587                                 if (NT_STATUS_IS_ERR(status)) {
588                                         DEBUG(0, ("failed to create edge set: "
589                                                   "%s\n", nt_errstr(status)));
590                                         talloc_free(graph);
591                                         talloc_free(tmp_ctx);
592                                         return status;
593                                 }
594
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) {
600                                         talloc_free(graph);
601                                         talloc_free(tmp_ctx);
602                                         return NT_STATUS_NO_MEMORY;
603                                 }
604                                 graph->edge_sets.data[graph->edge_sets.count] = *edge_set;
605                                 graph->edge_sets.count++;
606                         }
607                 }
608         }
609
610         *_graph = graph;
611         talloc_free(tmp_ctx);
612         return NT_STATUS_OK;
613 }