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/>.
23 #include "utils/net/net.h"
25 #include "lib/ldb/include/ldb.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "lib/util/util_ldb.h"
31 * Parses NTDS Settings DN to find out:
36 static bool net_drs_parse_ntds_dn(struct ldb_dn *ntds_dn,
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 ldb_dn_remove_child_components(dn, 1);
51 val = ldb_dn_get_rdn_val(dn);
52 *_dc_name = talloc_strdup(mem_ctx, (const char *)val->data);
55 /* remove DC and Servers components */
56 ldb_dn_remove_child_components(dn, 2);
58 val = ldb_dn_get_rdn_val(dn);
59 *_site_name = talloc_strdup(mem_ctx, (const char *)val->data);
62 if (_domain_dns_name) {
66 dns_name = ldb_dn_canonical_string(mem_ctx, dn);
67 NET_DRS_NOMEM_GOTO(dns_name, failed);
69 pstr = strchr(dns_name, '/');
74 *_domain_dns_name = dns_name;
85 static char * net_drs_dc_canonical_string(struct ldb_dn *ntds_dn, TALLOC_CTX *mem_ctx)
88 const char *site_name;
91 if (!net_drs_parse_ntds_dn(ntds_dn, mem_ctx, &dc_name, &site_name, NULL)) {
95 canonical_name = talloc_asprintf(mem_ctx, "%s\\%s", site_name, dc_name);
97 talloc_free(discard_const(dc_name));
98 talloc_free(discard_const(site_name));
100 return canonical_name;
104 * Prints DC information for showrepl command
106 static bool net_drs_showrepl_print_dc_info(struct net_drs_context *drs_ctx)
110 const char *site_name;
112 struct ldb_message **ntds_msgs;
113 struct ldb_message **site_msgs;
117 const char *ntds_attr[] = {"options", "objectGuid", "invocationId", NULL};
118 const char *site_ntds_attr[] = {"options", "whenChanged", NULL};
120 mem_ctx = talloc_new(drs_ctx);
122 /* Get NTDS Settings DN string for the DC */
123 dn = samdb_result_dn(drs_ctx->ldap.ldb, mem_ctx,
124 drs_ctx->ldap.rootdse, "dsServiceName", NULL);
125 NET_DRS_CHECK_GOTO(dn, failed, "No dsServiceName value in RootDSE!\n");
127 /* parse NTDS Settings DN */
128 if (!net_drs_parse_ntds_dn(dn, mem_ctx, &dc_name, &site_name, NULL)) {
129 d_printf("Unexptected: Failed to parse %s DN!\n",
130 ldb_dn_get_linearized(dn));
134 /* Query DC record for DSA's NTDS Settings DN */
135 ret = gendb_search_dn(drs_ctx->ldap.ldb, mem_ctx, dn, &ntds_msgs, ntds_attr);
137 d_printf("Error while fetching %s, Possible error: %s\n",
138 ldb_dn_get_linearized(dn),
139 ldb_errstring(drs_ctx->ldap.ldb));
143 /* find out NTDS Site Settings DN */
144 if (!ldb_dn_remove_child_components(dn, 3)) {
145 d_printf("Unexpected: ldb_dn_remove_child_components() failed!\n");
148 if (!ldb_dn_add_child_fmt(dn, "CN=%s", "NTDS Site Settings")) {
149 d_printf("Unexpected: ldb_dn_add_child_fmt() failed!\n");
152 /* Query Site record for DSA's NTDS Settings DN */
153 ret = gendb_search_dn(drs_ctx->ldap.ldb, mem_ctx, dn, &site_msgs, site_ntds_attr);
155 d_printf("Error while fetching %s, Possible error: %s\n",
156 ldb_dn_get_linearized(dn),
157 ldb_errstring(drs_ctx->ldap.ldb));
161 /* Site-name\DC-name */
162 d_printf("%s\\%s\n", site_name, dc_name);
164 options = samdb_result_uint(ntds_msgs[0], "options", 0);
166 /* TODO: Print options as string in IS_GC... etc form */
167 d_printf("DSA Options: 0x%08X\n", options);
169 d_printf("DSA Options: (none)\n");
172 options = samdb_result_uint(site_msgs[0], "options", 0);
174 /* TODO: Print options in string */
175 d_printf("DSA Options: 0x%08X\n", options);
177 d_printf("Site Options: (none)\n");
180 guid = samdb_result_guid(ntds_msgs[0], "objectGUID");
181 d_printf("DSA object GUID: %s\n", GUID_string(mem_ctx, &guid));
182 /* DSA invocationId */
183 guid = samdb_result_guid(ntds_msgs[0], "invocationId");
184 d_printf("DSA invocationID: %s\n", GUID_string(mem_ctx, &guid));
186 talloc_free(mem_ctx);
190 talloc_free(mem_ctx);
195 * Convenience function to call DsReplicaGetInfo
197 static bool net_drs_exec_DsReplicaGetInfo(struct net_drs_context *drs_ctx,
198 enum drsuapi_DsReplicaInfoType info_type,
199 union drsuapi_DsReplicaInfo *_replica_info)
202 struct drsuapi_DsReplicaGetInfo r;
203 union drsuapi_DsReplicaGetInfoRequest req;
204 enum drsuapi_DsReplicaInfoType info_type_got;
205 struct net_drs_connection *drs_conn = drs_ctx->drs_conn;
208 req.req1.info_type = info_type;
210 r.in.bind_handle = &drs_conn->bind_handle;
213 r.out.info = _replica_info;
214 r.out.info_type = &info_type_got;
216 status = dcerpc_drsuapi_DsReplicaGetInfo_r(drs_conn->drs_handle, drs_ctx, &r);
217 if (!NT_STATUS_IS_OK(status)) {
218 const char *errstr = nt_errstr(status);
219 d_printf("DsReplicaGetInfo failed - %s.\n", errstr);
221 } else if (!W_ERROR_IS_OK(r.out.result)) {
222 d_printf("DsReplicaGetInfo failed - %s.\n", win_errstr(r.out.result));
226 if (info_type != info_type_got) {
227 d_printf("DsReplicaGetInfo: Error requested info %d, got info %d",
228 info_type, info_type_got);
236 * Return transport type string for given transport object DN.
237 * Currently always return 'RPC'.
239 * TODO: Implement getting transport type for all kind of transports
242 net_drs_transport_type_str(struct net_drs_context *drs_ctx, const char *transport_obj_dn)
248 * Prints most of the info we got about
249 * a replication partner
251 static bool net_drs_showrepl_print_heighbor(struct net_drs_context *drs_ctx,
252 struct drsuapi_DsReplicaNeighbour *neighbor)
254 struct ldb_dn *ntds_dn;
257 mem_ctx = talloc_new(drs_ctx);
259 ntds_dn = ldb_dn_new(drs_ctx, drs_ctx->ldap.ldb, neighbor->source_dsa_obj_dn);
260 NET_DRS_NOMEM_GOTO(ntds_dn, failed);
262 d_printf("%s\n", neighbor->naming_context_dn);
263 /* TODO: Determine connection type */
264 d_printf("\t%s via %s\n",
265 net_drs_dc_canonical_string(ntds_dn, mem_ctx),
266 net_drs_transport_type_str(drs_ctx, neighbor->transport_obj_dn));
267 d_printf("\t\tDSA object GUID: %s\n", GUID_string(mem_ctx, &neighbor->source_dsa_obj_guid));
268 if (W_ERROR_IS_OK(neighbor->result_last_attempt)) {
269 d_printf("\t\tLast attempt @ %s was successful.\n",
270 nt_time_string(mem_ctx, neighbor->last_attempt));
272 d_printf("\t\tLast attempt @ %s failed, result %d (%s):\n",
273 nt_time_string(mem_ctx, neighbor->last_attempt),
274 W_ERROR_V(neighbor->result_last_attempt),
275 win_errstr(neighbor->result_last_attempt));
276 d_printf("\t\t\t%s\n", get_friendly_werror_msg(neighbor->result_last_attempt));
278 d_printf("\t\t%d consecutive failure(s).\n", neighbor->consecutive_sync_failures);
279 d_printf("\t\tLast success @ %s\n", nt_time_string(mem_ctx, neighbor->last_success));
281 talloc_free(mem_ctx);
285 talloc_free(mem_ctx);
290 * Prints list of all servers that target DC
293 static bool net_drs_showrepl_print_inbound_neihbors(struct net_drs_context *drs_ctx)
297 struct drsuapi_DsReplicaNeighbourCtr *reps_from;
298 union drsuapi_DsReplicaInfo replica_info;
300 d_printf("\n==== INBOUND NEIGHBORS ====\n");
302 bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
303 DRSUAPI_DS_REPLICA_INFO_NEIGHBORS, &replica_info);
305 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES");
308 reps_from = replica_info.neighbours;
310 for (i = 0; i < reps_from->count; i++) {
312 net_drs_showrepl_print_heighbor(drs_ctx, &reps_from->array[i]);
319 * Prints list of all servers that target DC
320 * notifies for changes
322 static bool net_drs_showrepl_print_outbound_neihbors(struct net_drs_context *drs_ctx)
326 struct drsuapi_DsReplicaNeighbourCtr *reps_to;
327 union drsuapi_DsReplicaInfo replica_info;
329 d_printf("\n==== OUTBOUND NEIGHBORS ====\n");
331 bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
332 DRSUAPI_DS_REPLICA_INFO_REPSTO, &replica_info);
334 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES");
337 reps_to = replica_info.repsto;
339 for (i = 0; i < reps_to->count; i++) {
341 net_drs_showrepl_print_heighbor(drs_ctx, &reps_to->array[i]);
348 * Prints all connections under
349 * NTDS Settings for target DC.
351 * NOTE: All connections are printed
352 * no matter what their status is
354 static bool net_drs_showrepl_print_connection_objects(struct net_drs_context *drs_ctx)
358 struct ldb_message **conn_msgs;
361 const char *dc_dns_name;
363 const char *conn_attr[] = {
367 "mS-DS-ReplicatesNCReason",
376 mem_ctx = talloc_new(drs_ctx);
378 d_printf("\n==== KCC CONNECTION OBJECTS ====\n");
380 /* Get NTDS Settings DN string for the DC */
381 dn = samdb_result_dn(drs_ctx->ldap.ldb, mem_ctx,
382 drs_ctx->ldap.rootdse, "dsServiceName", NULL);
383 NET_DRS_CHECK_GOTO(dn, failed, "No dsServiceName value in RootDSE!\n");
385 /* DNS host name for target DC */
386 dc_dns_name = samdb_result_string(drs_ctx->ldap.rootdse , "dnsHostName", NULL);
387 NET_DRS_CHECK_GOTO(dc_dns_name, failed, "No dsServiceName value in dnsHostName!\n");
389 /* Enum. Connection objects under NTDS Settings */
390 conn_count = gendb_search(drs_ctx->ldap.ldb, mem_ctx, dn,
391 &conn_msgs, conn_attr, "(objectClass=nTDSConnection)");
392 if (conn_count == -1) {
393 d_printf("Error searching Connections for %s, Possible error: %s\n",
394 ldb_dn_get_linearized(dn),
395 ldb_errstring(drs_ctx->ldap.ldb));
399 for (i = 0; i < conn_count; i++) {
401 const char *transport_type;
402 struct ldb_message_element *msg_elem;
403 struct ldb_message *conn_msg = conn_msgs[i];
405 d_printf("Connection --\n");
406 d_printf("\tConnection name : %s\n",
407 samdb_result_string(conn_msg, "name", NULL));
408 d_printf("\tEnabled : %s\n",
409 samdb_result_string(conn_msg, "enabledConnection", "TRUE"));
410 d_printf("\tServer DNS name : %s\n", dc_dns_name);
411 d_printf("\tServer DN name : %s\n",
412 samdb_result_string(conn_msg, "fromServer", NULL));
413 transport_type = samdb_result_string(conn_msg, "transportType", NULL);
414 d_printf("\t\tTransportType: %s\n",
415 net_drs_transport_type_str(drs_ctx, transport_type));
416 /* TODO: print Connection options in friendly format */
417 options = samdb_result_uint(conn_msg, "options", 0);
418 d_printf("\t\toptions: 0x%08X\n", options);
420 /* print replicated NCs for this connection */
421 msg_elem = ldb_msg_find_element(conn_msg, "mS-DS-ReplicatesNCReason");
423 d_printf("Warning: No NC replicated for Connection!\n");
426 for (k = 0; k < msg_elem->num_values; k++) {
427 struct dsdb_dn *bin_dn;
429 bin_dn = dsdb_dn_parse(mem_ctx, drs_ctx->ldap.ldb,
430 &msg_elem->values[k], DSDB_SYNTAX_BINARY_DN);
432 d_printf("Unexpected: Failed to parse DN - %s\n",
433 msg_elem->values[k].data);
435 d_printf("\t\tReplicatesNC: %s\n", ldb_dn_get_linearized(bin_dn->dn));
436 /* TODO: print Reason flags in friendly format */
437 options = RIVAL(bin_dn->extra_part.data, 0);
438 d_printf("\t\tReason: 0x%08X\n", options);
439 d_printf("\t\t\tReplica link has been added.\n");
443 talloc_free(mem_ctx);
447 talloc_free(mem_ctx);
452 * Prints all DC's connections failure.
454 * NOTE: Still don't know exactly what
455 * this information means
457 static bool net_drs_showrepl_print_connect_failures(struct net_drs_context *drs_ctx)
461 struct ldb_dn *ntds_dn;
462 struct drsuapi_DsReplicaKccDsaFailure *failure;
463 struct drsuapi_DsReplicaKccDsaFailuresCtr *connect_failures;
464 union drsuapi_DsReplicaInfo replica_info;
467 d_printf("\n==== CONNECION FAILURES ====\n");
469 bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
470 DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES,
473 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES");
476 connect_failures = replica_info.connectfailures;
478 mem_ctx = talloc_new(drs_ctx);
480 for (i = 0; i < connect_failures->count; i++) {
481 failure = &connect_failures->array[i];
483 ntds_dn = ldb_dn_new(mem_ctx, drs_ctx->ldap.ldb, failure->dsa_obj_dn);
484 d_printf("Source: %s\n", net_drs_dc_canonical_string(ntds_dn, mem_ctx));
485 d_printf("******* %d CONSECUTIVE FAILURES since %s\n",
486 failure->num_failures,
487 nt_time_string(mem_ctx, failure->first_failure));
488 d_printf("Last error: %d (%s):",
489 W_ERROR_V(failure->last_result),
490 win_errstr(failure->last_result));
491 d_printf("\t\t\t%s\n", get_friendly_werror_msg(failure->last_result));
494 talloc_free(mem_ctx);
499 * Prints all DC's link failures
501 static bool net_drs_showrepl_print_link_failures(struct net_drs_context *drs_ctx)
505 struct ldb_dn *ntds_dn;
506 struct drsuapi_DsReplicaKccDsaFailure *failure;
507 struct drsuapi_DsReplicaKccDsaFailuresCtr *link_failures;
508 union drsuapi_DsReplicaInfo replica_info;
511 d_printf("\n==== LINK FAILURES ====\n");
513 bret = net_drs_exec_DsReplicaGetInfo(drs_ctx,
514 DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES, &replica_info);
516 d_printf("DsReplicaGetInfo() failed for DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES");
519 link_failures = replica_info.linkfailures;
521 mem_ctx = talloc_new(drs_ctx);
523 for (i = 0; i < link_failures->count; i++) {
524 failure = &link_failures->array[i];
526 ntds_dn = ldb_dn_new(mem_ctx, drs_ctx->ldap.ldb, failure->dsa_obj_dn);
527 d_printf("Source: %s\n", net_drs_dc_canonical_string(ntds_dn, mem_ctx));
528 d_printf("******* %d CONSECUTIVE FAILURES since %s\n",
529 failure->num_failures,
530 nt_time_string(mem_ctx, failure->first_failure));
531 d_printf("Last error: %d (%s):\n",
532 W_ERROR_V(failure->last_result),
533 win_errstr(failure->last_result));
534 d_printf("\t\t\t%s\n", get_friendly_werror_msg(failure->last_result));
537 talloc_free(mem_ctx);
542 * 'net drs showrepl' command entry point
544 int net_drs_showrepl_cmd(struct net_context *ctx, int argc, const char **argv)
547 struct net_drs_context *drs_ctx = NULL;
549 /* only one arg expected */
551 return net_drs_showrepl_usage(ctx, argc, argv);
556 if (!net_drs_create_context(ctx, dc_name, &drs_ctx)) {
560 /* Print DC and Site info */
561 if (!net_drs_showrepl_print_dc_info(drs_ctx)) {
565 /* INBOUND Neighbors */
566 if (!net_drs_showrepl_print_inbound_neihbors(drs_ctx)) {
570 /* OUTBOUND Neighbors */
571 if (!net_drs_showrepl_print_outbound_neihbors(drs_ctx)) {
575 /* Connection objects for DC */
576 if (!net_drs_showrepl_print_connection_objects(drs_ctx)) {
580 /* Connection failures */
581 if (!net_drs_showrepl_print_connect_failures(drs_ctx)) {
586 if (!net_drs_showrepl_print_link_failures(drs_ctx)) {
590 talloc_free(drs_ctx);
594 talloc_free(drs_ctx);
599 * 'net drs showrepl' usage
601 int net_drs_showrepl_usage(struct net_context *ctx, int argc, const char **argv)
603 d_printf("net drs showrepl <DC_NAME>\n");