s4-samba-tool: switched over to python version of samba-tool drs
[Samba/gebeck_regimport.git] / source4 / samba_tool / drs / drs_showrepl.c
blob5c095a89e674db09ce59447de4b55eb4e3348625
1 /*
2 Unix SMB/CIFS implementation.
4 Implements functions offered by repadmin.exe tool under Windows
6 Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 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/>.
22 #include "includes.h"
23 #include "samba_tool/samba_tool.h"
24 #include "samba_tool/drs/drs.h"
25 #include "lib/ldb/include/ldb.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "lib/util/util_ldb.h"
30 /**
31 * Parses NTDS Settings DN to find out:
32 * - DC name
33 * - Site name
34 * - Domain DNS name
36 static bool net_drs_parse_ntds_dn(struct ldb_dn *ntds_dn,
37 TALLOC_CTX *mem_ctx,
38 const char **_dc_name,
39 const char **_site_name,
40 const char **_domain_dns_name)
42 struct ldb_dn *dn = NULL;
43 const struct ldb_val *val;
45 dn = ldb_dn_copy(mem_ctx, ntds_dn);
46 NET_DRS_NOMEM_GOTO(dn, failed);
48 /* remove NTDS Settings component */
49 if (!ldb_dn_remove_child_components(dn, 1)) {
50 return false;
52 if (_dc_name) {
53 val = ldb_dn_get_rdn_val(dn);
54 *_dc_name = talloc_strdup(mem_ctx, (const char *)val->data);
57 /* remove DC and Servers components */
58 ldb_dn_remove_child_components(dn, 2);
59 if (_site_name) {
60 val = ldb_dn_get_rdn_val(dn);
61 *_site_name = talloc_strdup(mem_ctx, (const char *)val->data);
64 if (_domain_dns_name) {
65 char *pstr;
66 char *dns_name;
68 dns_name = ldb_dn_canonical_string(mem_ctx, dn);
69 NET_DRS_NOMEM_GOTO(dns_name, failed);
71 pstr = strchr(dns_name, '/');
72 if (pstr) {
73 *pstr = '\0';
76 *_domain_dns_name = dns_name;
79 talloc_free(dn);
80 return true;
82 failed:
83 talloc_free(dn);
84 return false;
87 static const char * net_drs_dc_canonical_string(struct ldb_dn *ntds_dn, TALLOC_CTX *mem_ctx)
89 const char *dc_name;
90 const char *site_name;
91 char *canonical_name;
93 if (ldb_dn_is_null(ntds_dn)) {
94 return "(NULL DN)";
97 if (!net_drs_parse_ntds_dn(ntds_dn, mem_ctx, &dc_name, &site_name, NULL)) {
98 return NULL;
101 canonical_name = talloc_asprintf(mem_ctx, "%s\\%s", site_name, dc_name);
103 talloc_free(discard_const(dc_name));
104 talloc_free(discard_const(site_name));
106 return canonical_name;
110 * Prints DC information for showrepl command
112 static bool net_drs_showrepl_print_dc_info(struct net_drs_context *drs_ctx)
114 int ret;
115 const char *dc_name;
116 const char *site_name;
117 struct ldb_dn *dn;
118 struct ldb_message **ntds_msgs;
119 struct ldb_message **site_msgs;
120 uint32_t options;
121 struct GUID guid;
122 TALLOC_CTX *mem_ctx;
123 const char *ntds_attr[] = {"options", "objectGuid", "invocationId", NULL};
124 const char *site_ntds_attr[] = {"options", "whenChanged", NULL};
126 mem_ctx = talloc_new(drs_ctx);
128 /* Get NTDS Settings DN string for the DC */
129 dn = samdb_result_dn(drs_ctx->ldap.ldb, mem_ctx,
130 drs_ctx->ldap.rootdse, "dsServiceName", NULL);
131 NET_DRS_CHECK_GOTO(dn, failed, "No dsServiceName value in RootDSE!\n");
133 /* parse NTDS Settings DN */
134 if (!net_drs_parse_ntds_dn(dn, mem_ctx, &dc_name, &site_name, NULL)) {
135 d_printf("Unexpected: Failed to parse %s DN!\n",
136 ldb_dn_get_linearized(dn));
137 goto failed;
140 /* Query DC record for DSA's NTDS Settings DN */
141 ret = gendb_search_dn(drs_ctx->ldap.ldb, mem_ctx, dn, &ntds_msgs, ntds_attr);
142 if (ret != 1) {
143 d_printf("Error while fetching %s, Possible error: %s\n",
144 ldb_dn_get_linearized(dn),
145 ldb_errstring(drs_ctx->ldap.ldb));
146 goto failed;
149 /* find out NTDS Site Settings DN */
150 if (!ldb_dn_remove_child_components(dn, 3)) {
151 d_printf("Unexpected: ldb_dn_remove_child_components() failed!\n");
152 goto failed;
154 if (!ldb_dn_add_child_fmt(dn, "CN=%s", "NTDS Site Settings")) {
155 d_printf("Unexpected: ldb_dn_add_child_fmt() failed!\n");
156 goto failed;
158 /* Query Site record for DSA's NTDS Settings DN */
159 ret = gendb_search_dn(drs_ctx->ldap.ldb, mem_ctx, dn, &site_msgs, site_ntds_attr);
160 if (ret != 1) {
161 d_printf("Error while fetching %s, Possible error: %s\n",
162 ldb_dn_get_linearized(dn),
163 ldb_errstring(drs_ctx->ldap.ldb));
164 goto failed;
167 /* Site-name\DC-name */
168 d_printf("%s\\%s\n", site_name, dc_name);
169 /* DSA Options */
170 options = ldb_msg_find_attr_as_uint(ntds_msgs[0], "options", 0);
171 if (options) {
172 /* TODO: Print options as string in IS_GC... etc form */
173 d_printf("DSA Options: 0x%08X\n", options);
174 } else {
175 d_printf("DSA Options: (none)\n");
177 /* Site Options */
178 options = ldb_msg_find_attr_as_uint(site_msgs[0], "options", 0);
179 if (options) {
180 /* TODO: Print options in string */
181 d_printf("DSA Options: 0x%08X\n", options);
182 } else {
183 d_printf("Site Options: (none)\n");
185 /* DSA GUID */
186 guid = samdb_result_guid(ntds_msgs[0], "objectGUID");
187 d_printf("DSA object GUID: %s\n", GUID_string(mem_ctx, &guid));
188 /* DSA invocationId */
189 guid = samdb_result_guid(ntds_msgs[0], "invocationId");
190 d_printf("DSA invocationID: %s\n", GUID_string(mem_ctx, &guid));
192 talloc_free(mem_ctx);
193 return true;
195 failed:
196 talloc_free(mem_ctx);
197 return false;
201 * Convenience function to call DsReplicaGetInfo
203 static bool net_drs_exec_DsReplicaGetInfo(struct net_drs_context *drs_ctx,
204 enum drsuapi_DsReplicaInfoType info_type,
205 union drsuapi_DsReplicaInfo *_replica_info)
207 NTSTATUS status;
208 struct drsuapi_DsReplicaGetInfo r;
209 union drsuapi_DsReplicaGetInfoRequest req;
210 enum drsuapi_DsReplicaInfoType info_type_got;
211 struct net_drs_connection *drs_conn = drs_ctx->drs_conn;
213 ZERO_STRUCT(req);
214 req.req1.info_type = info_type;
216 r.in.bind_handle = &drs_conn->bind_handle;
217 r.in.level = 1;
218 r.in.req = &req;
219 r.out.info = _replica_info;
220 r.out.info_type = &info_type_got;
222 status = dcerpc_drsuapi_DsReplicaGetInfo_r(drs_conn->drs_handle, drs_ctx, &r);
223 if (!NT_STATUS_IS_OK(status)) {
224 const char *errstr = nt_errstr(status);
225 d_printf("DsReplicaGetInfo failed - %s.\n", errstr);
226 return false;
227 } else if (!W_ERROR_IS_OK(r.out.result)) {
228 d_printf("DsReplicaGetInfo failed - %s.\n", win_errstr(r.out.result));
229 return false;
232 if (info_type != info_type_got) {
233 d_printf("DsReplicaGetInfo: Error requested info %d, got info %d.\n",
234 info_type, info_type_got);
235 return false;
238 return true;
242 * Return transport type string for given transport object DN.
243 * Currently always return 'RPC'.
245 * TODO: Implement getting transport type for all kind of transports
247 static const char *
248 net_drs_transport_type_str(struct net_drs_context *drs_ctx, const char *transport_obj_dn)
250 return "RPC";
254 * Prints most of the info we got about
255 * a replication partner
257 static bool net_drs_showrepl_print_neighbor(struct net_drs_context *drs_ctx,
258 struct drsuapi_DsReplicaNeighbour *neighbor)
260 struct ldb_dn *ntds_dn;
261 TALLOC_CTX *mem_ctx;
263 mem_ctx = talloc_new(drs_ctx);
265 ntds_dn = ldb_dn_new(drs_ctx, drs_ctx->ldap.ldb, neighbor->source_dsa_obj_dn);
266 NET_DRS_NOMEM_GOTO(ntds_dn, failed);
268 d_printf("%s\n", neighbor->naming_context_dn);
269 /* TODO: Determine connection type */
270 d_printf("\t%s via %s\n",
271 net_drs_dc_canonical_string(ntds_dn, mem_ctx),
272 net_drs_transport_type_str(drs_ctx, neighbor->transport_obj_dn));
273 d_printf("\t\tDSA object GUID: %s\n", GUID_string(mem_ctx, &neighbor->source_dsa_obj_guid));
274 if (W_ERROR_IS_OK(neighbor->result_last_attempt)) {
275 d_printf("\t\tLast attempt @ %s was successful.\n",
276 nt_time_string(mem_ctx, neighbor->last_attempt));
277 } else {
278 d_printf("\t\tLast attempt @ %s failed, result %d (%s):\n",
279 nt_time_string(mem_ctx, neighbor->last_attempt),
280 W_ERROR_V(neighbor->result_last_attempt),
281 win_errstr(neighbor->result_last_attempt));
282 d_printf("\t\t\t%s\n", get_friendly_werror_msg(neighbor->result_last_attempt));
284 d_printf("\t\t%d consecutive failure(s).\n", neighbor->consecutive_sync_failures);
285 d_printf("\t\tLast success @ %s\n", nt_time_string(mem_ctx, neighbor->last_success));
287 talloc_free(mem_ctx);
288 return true;
290 failed:
291 talloc_free(mem_ctx);
292 return false;
296 * Prints list of all servers that target DC
297 * replicates from
299 static bool net_drs_showrepl_print_inbound_neihbors(struct net_drs_context *drs_ctx)
301 int i;
302 bool bret;
303 struct drsuapi_DsReplicaNeighbourCtr *reps_from;
304 union drsuapi_DsReplicaInfo replica_info;
306 d_printf("\n==== INBOUND NEIGHBORS ====\n");
308 bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
309 DRSUAPI_DS_REPLICA_INFO_NEIGHBORS, &replica_info);
310 if (!bret) {
311 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES.\n");
312 return false;
314 reps_from = replica_info.neighbours;
316 for (i = 0; i < reps_from->count; i++) {
317 d_printf("\n");
318 net_drs_showrepl_print_neighbor(drs_ctx, &reps_from->array[i]);
321 return true;
325 * Prints list of all servers that target DC
326 * notifies for changes
328 static bool net_drs_showrepl_print_outbound_neihbors(struct net_drs_context *drs_ctx)
330 int i;
331 bool bret;
332 struct drsuapi_DsReplicaNeighbourCtr *reps_to;
333 union drsuapi_DsReplicaInfo replica_info;
335 d_printf("\n==== OUTBOUND NEIGHBORS ====\n");
337 bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
338 DRSUAPI_DS_REPLICA_INFO_REPSTO, &replica_info);
339 if (!bret) {
340 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES.\n");
341 return false;
343 reps_to = replica_info.repsto;
345 for (i = 0; i < reps_to->count; i++) {
346 d_printf("\n");
347 net_drs_showrepl_print_neighbor(drs_ctx, &reps_to->array[i]);
350 return true;
354 * Prints all connections under
355 * NTDS Settings for target DC.
357 * NOTE: All connections are printed
358 * no matter what their status is
360 static bool net_drs_showrepl_print_connection_objects(struct net_drs_context *drs_ctx)
362 int i;
363 int conn_count;
364 struct ldb_message **conn_msgs;
365 struct ldb_dn *dn;
366 uint32_t options;
367 const char *dc_dns_name;
368 TALLOC_CTX *mem_ctx;
369 const char *conn_attr[] = {
370 "name",
371 "enabledConnection",
372 "fromServer",
373 "mS-DS-ReplicatesNCReason",
374 "options",
375 "schedule",
376 "transportType",
377 "whenChanged",
378 "whenCreated",
379 NULL
382 mem_ctx = talloc_new(drs_ctx);
384 d_printf("\n==== KCC CONNECTION OBJECTS ====\n");
386 /* Get NTDS Settings DN string for the DC */
387 dn = samdb_result_dn(drs_ctx->ldap.ldb, mem_ctx,
388 drs_ctx->ldap.rootdse, "dsServiceName", NULL);
389 NET_DRS_CHECK_GOTO(dn, failed, "No dsServiceName value in RootDSE!\n");
391 /* DNS host name for target DC */
392 dc_dns_name = ldb_msg_find_attr_as_string(drs_ctx->ldap.rootdse , "dnsHostName", NULL);
393 NET_DRS_CHECK_GOTO(dc_dns_name, failed, "No dsServiceName value in dnsHostName!\n");
395 /* Enum. Connection objects under NTDS Settings */
396 conn_count = gendb_search(drs_ctx->ldap.ldb, mem_ctx, dn,
397 &conn_msgs, conn_attr, "(objectClass=nTDSConnection)");
398 if (conn_count == -1) {
399 d_printf("Error searching Connections for %s, Possible error: %s\n",
400 ldb_dn_get_linearized(dn),
401 ldb_errstring(drs_ctx->ldap.ldb));
402 goto failed;
405 for (i = 0; i < conn_count; i++) {
406 int k;
407 const char *transport_type;
408 struct ldb_message_element *msg_elem;
409 struct ldb_message *conn_msg = conn_msgs[i];
411 d_printf("Connection --\n");
412 d_printf("\tConnection name : %s\n",
413 ldb_msg_find_attr_as_string(conn_msg, "name", NULL));
414 d_printf("\tEnabled : %s\n",
415 ldb_msg_find_attr_as_string(conn_msg, "enabledConnection", "TRUE"));
416 d_printf("\tServer DNS name : %s\n", dc_dns_name);
417 d_printf("\tServer DN name : %s\n",
418 ldb_msg_find_attr_as_string(conn_msg, "fromServer", NULL));
419 transport_type = ldb_msg_find_attr_as_string(conn_msg, "transportType", NULL);
420 d_printf("\t\tTransportType: %s\n",
421 net_drs_transport_type_str(drs_ctx, transport_type));
422 /* TODO: print Connection options in friendly format */
423 options = ldb_msg_find_attr_as_uint(conn_msg, "options", 0);
424 d_printf("\t\toptions: 0x%08X\n", options);
426 /* print replicated NCs for this connection */
427 msg_elem = ldb_msg_find_element(conn_msg, "mS-DS-ReplicatesNCReason");
428 if (!msg_elem) {
429 d_printf("Warning: No NC replicated for Connection!\n");
430 continue;
432 for (k = 0; k < msg_elem->num_values; k++) {
433 struct dsdb_dn *bin_dn;
435 bin_dn = dsdb_dn_parse(mem_ctx, drs_ctx->ldap.ldb,
436 &msg_elem->values[k], DSDB_SYNTAX_BINARY_DN);
437 if (!bin_dn) {
438 d_printf("Unexpected: Failed to parse DN - %s\n",
439 msg_elem->values[k].data);
441 d_printf("\t\tReplicatesNC: %s\n", ldb_dn_get_linearized(bin_dn->dn));
442 /* TODO: print Reason flags in friendly format */
443 options = RIVAL(bin_dn->extra_part.data, 0);
444 d_printf("\t\tReason: 0x%08X\n", options);
445 d_printf("\t\t\tReplica link has been added.\n");
449 talloc_free(mem_ctx);
450 return true;
452 failed:
453 talloc_free(mem_ctx);
454 return false;
458 * Prints all DC's connections failure.
460 * NOTE: Still don't know exactly what
461 * this information means
463 static bool net_drs_showrepl_print_connect_failures(struct net_drs_context *drs_ctx)
465 int i;
466 bool bret;
467 struct ldb_dn *ntds_dn;
468 struct drsuapi_DsReplicaKccDsaFailure *failure;
469 struct drsuapi_DsReplicaKccDsaFailuresCtr *connect_failures;
470 union drsuapi_DsReplicaInfo replica_info;
471 TALLOC_CTX *mem_ctx;
473 d_printf("\n==== CONNECION FAILURES ====\n");
475 bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
476 DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES,
477 &replica_info);
478 if (!bret) {
479 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES.\n");
480 return false;
482 connect_failures = replica_info.connectfailures;
484 mem_ctx = talloc_new(drs_ctx);
486 for (i = 0; i < connect_failures->count; i++) {
487 failure = &connect_failures->array[i];
489 ntds_dn = ldb_dn_new(mem_ctx, drs_ctx->ldap.ldb, failure->dsa_obj_dn);
490 d_printf("Source: %s\n", net_drs_dc_canonical_string(ntds_dn, mem_ctx));
491 d_printf("******* %d CONSECUTIVE FAILURES since %s\n",
492 failure->num_failures,
493 nt_time_string(mem_ctx, failure->first_failure));
494 d_printf("Last error: %d (%s):\n",
495 W_ERROR_V(failure->last_result),
496 win_errstr(failure->last_result));
497 d_printf("\t\t\t%s\n", get_friendly_werror_msg(failure->last_result));
500 talloc_free(mem_ctx);
501 return true;
505 * Prints all DC's link failures
507 static bool net_drs_showrepl_print_link_failures(struct net_drs_context *drs_ctx)
509 int i;
510 bool bret;
511 struct ldb_dn *ntds_dn;
512 struct drsuapi_DsReplicaKccDsaFailure *failure;
513 struct drsuapi_DsReplicaKccDsaFailuresCtr *link_failures;
514 union drsuapi_DsReplicaInfo replica_info;
515 TALLOC_CTX *mem_ctx;
517 d_printf("\n==== LINK FAILURES ====\n");
519 bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
520 DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES, &replica_info);
521 if (!bret) {
522 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES.\n");
523 return false;
525 link_failures = replica_info.linkfailures;
527 mem_ctx = talloc_new(drs_ctx);
529 for (i = 0; i < link_failures->count; i++) {
530 failure = &link_failures->array[i];
532 ntds_dn = ldb_dn_new(mem_ctx, drs_ctx->ldap.ldb, failure->dsa_obj_dn);
533 d_printf("Source: %s\n", net_drs_dc_canonical_string(ntds_dn, mem_ctx));
534 d_printf("******* %d CONSECUTIVE FAILURES since %s\n",
535 failure->num_failures,
536 nt_time_string(mem_ctx, failure->first_failure));
537 d_printf("Last error: %d (%s):\n",
538 W_ERROR_V(failure->last_result),
539 win_errstr(failure->last_result));
540 d_printf("\t\t\t%s\n", get_friendly_werror_msg(failure->last_result));
543 talloc_free(mem_ctx);
544 return true;
548 * 'samba-tool drs showrepl' command entry point
550 int net_drs_showrepl_cmd(struct net_context *ctx, int argc, const char **argv)
552 const char *dc_name;
553 struct net_drs_context *drs_ctx = NULL;
555 /* only one arg expected */
556 if (argc != 1) {
557 return net_drs_showrepl_usage(ctx, argc, argv);
560 dc_name = argv[0];
562 if (!net_drs_create_context(ctx, dc_name, &drs_ctx)) {
563 goto failed;
566 /* Print DC and Site info */
567 if (!net_drs_showrepl_print_dc_info(drs_ctx)) {
568 goto failed;
571 /* INBOUND Neighbors */
572 if (!net_drs_showrepl_print_inbound_neihbors(drs_ctx)) {
573 goto failed;
576 /* OUTBOUND Neighbors */
577 if (!net_drs_showrepl_print_outbound_neihbors(drs_ctx)) {
578 goto failed;
581 /* Connection objects for DC */
582 if (!net_drs_showrepl_print_connection_objects(drs_ctx)) {
583 goto failed;
586 /* Connection failures */
587 if (!net_drs_showrepl_print_connect_failures(drs_ctx)) {
588 goto failed;
591 /* Link failures */
592 if (!net_drs_showrepl_print_link_failures(drs_ctx)) {
593 goto failed;
596 talloc_free(drs_ctx);
597 return 0;
599 failed:
600 talloc_free(drs_ctx);
601 return -1;
605 * 'samba-tool drs showrepl' usage
607 int net_drs_showrepl_usage(struct net_context *ctx, int argc, const char **argv)
609 d_printf("samba-tool drs showrepl <DC_NAME>\n");
610 return 0;