diff options
author | Volker Lendecke <vl@samba.org> | 2011-01-10 17:25:00 +0100 |
---|---|---|
committer | Volker Lendecke <vlendec@samba.org> | 2011-01-19 08:40:28 +0100 |
commit | 7f87d58900c2adf4d79f4dc7859a96f1d00d819b (patch) | |
tree | add63dc353d4026745f7ffb3c4fcbf8473737e56 | |
parent | 1b439960224cb97e328204cdf79654f565043679 (diff) | |
download | samba-7f87d58900c2adf4d79f4dc7859a96f1d00d819b.tar.gz samba-7f87d58900c2adf4d79f4dc7859a96f1d00d819b.tar.bz2 samba-7f87d58900c2adf4d79f4dc7859a96f1d00d819b.zip |
s3: Add wbinfo --dc-info
wbinfo --dc-info prints the current DC name and IP address. This helps
diagnosing problems that might happen when a later wbinfo --ping-dc fails.
This patch started out by using the SAF and NBT cache entires, but those are
relatively short-lived. So I decided to invent a new gencache entry with a very
long timeout. We need to go via the gencache because when for some reason a
winbind child process is stuck, we can't query it for the current DC it's
connected to. This must eventually go away again when we have a fully async
winbind.
Autobuild-User: Volker Lendecke <vlendec@samba.org>
Autobuild-Date: Wed Jan 19 08:40:28 CET 2011 on sn-devel-104
-rw-r--r-- | nsswitch/libwbclient/wbc_util.c | 86 | ||||
-rw-r--r-- | nsswitch/libwbclient/wbclient.h | 15 | ||||
-rw-r--r-- | nsswitch/wbinfo.c | 33 | ||||
-rw-r--r-- | nsswitch/winbind_struct_protocol.h | 4 | ||||
-rw-r--r-- | source3/winbindd/winbindd.c | 1 | ||||
-rw-r--r-- | source3/winbindd/winbindd_cm.c | 93 | ||||
-rw-r--r-- | source3/winbindd/winbindd_misc.c | 50 | ||||
-rw-r--r-- | source3/winbindd/winbindd_proto.h | 4 |
8 files changed, 284 insertions, 2 deletions
diff --git a/nsswitch/libwbclient/wbc_util.c b/nsswitch/libwbclient/wbc_util.c index e2e657a903..d2783f30d2 100644 --- a/nsswitch/libwbclient/wbc_util.c +++ b/nsswitch/libwbclient/wbc_util.c @@ -203,6 +203,92 @@ wbcErr wbcDomainInfo(const char *domain, struct wbcDomainInfo **dinfo) return wbc_status; } +/* Get the list of current DCs */ +wbcErr wbcDcInfo(const char *domain, size_t *num_dcs, + const char ***dc_names, const char ***dc_ips) +{ + struct winbindd_request request; + struct winbindd_response response; + const char **names = NULL; + const char **ips = NULL; + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + size_t extra_len; + int i; + char *p; + + /* Initialise request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + if (domain != NULL) { + strncpy(request.domain_name, domain, + sizeof(request.domain_name) - 1); + } + + wbc_status = wbcRequestResponse(WINBINDD_DC_INFO, + &request, &response); + BAIL_ON_WBC_ERROR(wbc_status); + + names = wbcAllocateStringArray(response.data.num_entries); + BAIL_ON_PTR_ERROR(names, wbc_status); + + ips = wbcAllocateStringArray(response.data.num_entries); + BAIL_ON_PTR_ERROR(names, wbc_status); + + wbc_status = WBC_ERR_INVALID_RESPONSE; + + p = (char *)response.extra_data.data; + + if (response.length < (sizeof(struct winbindd_response)+1)) { + goto done; + } + + extra_len = response.length - sizeof(struct winbindd_response); + + if (p[extra_len-1] != '\0') { + goto done; + } + + for (i=0; i<response.data.num_entries; i++) { + char *q; + + q = strchr(p, '\n'); + if (q == NULL) { + goto done; + } + names[i] = strndup(p, q-p); + BAIL_ON_PTR_ERROR(names[i], wbc_status); + p = q+1; + + q = strchr(p, '\n'); + if (q == NULL) { + goto done; + } + ips[i] = strndup(p, q-p); + BAIL_ON_PTR_ERROR(ips[i], wbc_status); + p = q+1; + } + if (p[0] != '\0') { + goto done; + } + + wbc_status = WBC_ERR_SUCCESS; +done: + if (response.extra_data.data) + free(response.extra_data.data); + + if (WBC_ERROR_IS_OK(wbc_status)) { + *num_dcs = response.data.num_entries; + *dc_names = names; + names = NULL; + *dc_ips = ips; + ips = NULL; + } + wbcFreeMemory(names); + wbcFreeMemory(ips); + return wbc_status; +} /* Resolve a NetbiosName via WINS */ wbcErr wbcResolveWinsByName(const char *name, char **ip) diff --git a/nsswitch/libwbclient/wbclient.h b/nsswitch/libwbclient/wbclient.h index e2f989094a..39670ab6ed 100644 --- a/nsswitch/libwbclient/wbclient.h +++ b/nsswitch/libwbclient/wbclient.h @@ -192,7 +192,6 @@ struct wbcDomainInfo { #define WBC_DOMINFO_TRUSTTYPE_IN_FOREST 0x00000002 #define WBC_DOMINFO_TRUSTTYPE_EXTERNAL 0x00000003 - /** * @brief Auth User Parameters **/ @@ -992,6 +991,20 @@ wbcErr wbcDomainInfo(const char *domain, struct wbcDomainInfo **dinfo); /** + * @brief Lookup the currently contacted DCs + * + * @param domain The domain to query + * + * @param num_dcs Number of DCs currently known + * @param dc_names Names of the currently known DCs + * @param dc_ips IP addresses of the currently known DCs + * + * @return #wbcErr + **/ +wbcErr wbcDcInfo(const char *domain, size_t *num_dcs, + const char ***dc_names, const char ***dc_ips); + +/** * @brief Enumerate the domain trusts known by Winbind * * @param **domains Pointer to the allocated domain list array diff --git a/nsswitch/wbinfo.c b/nsswitch/wbinfo.c index 6ff66f8f3c..caa37f3c9e 100644 --- a/nsswitch/wbinfo.c +++ b/nsswitch/wbinfo.c @@ -787,6 +787,31 @@ static bool wbinfo_check_secret(const char *domain) return true; } +/* Find the currently connected DCs */ + +static bool wbinfo_dc_info(const char *domain_name) +{ + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + size_t i, num_dcs; + const char **dc_names, **dc_ips; + + wbc_status = wbcDcInfo(domain_name, &num_dcs, + &dc_names, &dc_ips); + if (!WBC_ERROR_IS_OK(wbc_status)) { + printf("Could not find dc info %s\n", + domain_name ? domain_name : "our domain"); + return false; + } + + for (i=0; i<num_dcs; i++) { + printf("%s (%s)\n", dc_names[i], dc_ips[i]); + } + wbcFreeMemory(dc_names); + wbcFreeMemory(dc_ips); + + return true; +} + /* Change trust account password */ static bool wbinfo_change_secret(const char *domain) @@ -1921,6 +1946,7 @@ enum { OPT_SEQUENCE, OPT_GETDCNAME, OPT_DSGETDCNAME, + OPT_DC_INFO, OPT_USERDOMGROUPS, OPT_SIDALIASES, OPT_USERSIDS, @@ -2030,6 +2056,8 @@ int main(int argc, char **argv, char **envp) { "getdcname", 0, POPT_ARG_STRING, &string_arg, OPT_GETDCNAME, "Get a DC name for a foreign domain", "domainname" }, { "dsgetdcname", 0, POPT_ARG_STRING, &string_arg, OPT_DSGETDCNAME, "Find a DC for a domain", "domainname" }, + { "dc-info", 0, POPT_ARG_STRING, &string_arg, OPT_DC_INFO, + "Find the currently known DCs", "domainname" }, { "get-auth-user", 0, POPT_ARG_NONE, NULL, OPT_GET_AUTH_USER, "Retrieve user and password used by winbindd (root only)", NULL }, { "ping", 'p', POPT_ARG_NONE, 0, 'p', "Ping winbindd to see if it is alive" }, { "domain", 0, POPT_ARG_STRING, &opt_domain_name, OPT_DOMAIN_NAME, "Define to the domain to restrict operation", "domain" }, @@ -2443,6 +2471,11 @@ int main(int argc, char **argv, char **envp) goto done; } break; + case OPT_DC_INFO: + if (!wbinfo_dc_info(string_arg)) { + goto done; + } + break; case OPT_SEPARATOR: { const char sep = winbind_separator(); if ( !sep ) { diff --git a/nsswitch/winbind_struct_protocol.h b/nsswitch/winbind_struct_protocol.h index 537754f688..9304702fa0 100644 --- a/nsswitch/winbind_struct_protocol.h +++ b/nsswitch/winbind_struct_protocol.h @@ -54,8 +54,9 @@ typedef char fstring[FSTRING_LEN]; * 25: removed WINBINDD_SET_HWM * removed WINBINDD_SET_MAPPING * removed WINBINDD_REMOVE_MAPPING + * 26: added WINBINDD_DC_INFO */ -#define WINBIND_INTERFACE_VERSION 25 +#define WINBIND_INTERFACE_VERSION 26 /* Have to deal with time_t being 4 or 8 bytes due to structure alignment. On a 64bit Linux box, we have to support a constant structure size @@ -132,6 +133,7 @@ enum winbindd_cmd { struct winbindd_domain */ WINBINDD_GETDCNAME, /* Issue a GetDCName Request */ WINBINDD_DSGETDCNAME, /* Issue a DsGetDCName Request */ + WINBINDD_DC_INFO, /* Which DC are we connected to? */ WINBINDD_SHOW_SEQUENCE, /* display sequence numbers of domains */ diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c index e5aeca6830..8f4a205db5 100644 --- a/source3/winbindd/winbindd.c +++ b/source3/winbindd/winbindd.c @@ -442,6 +442,7 @@ static struct winbindd_dispatch_table { "INTERFACE_VERSION" }, { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" }, { WINBINDD_DOMAIN_INFO, winbindd_domain_info, "DOMAIN_INFO" }, + { WINBINDD_DC_INFO, winbindd_dc_info, "DC_INFO" }, { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" }, { WINBINDD_PRIV_PIPE_DIR, winbindd_priv_pipe_dir, "WINBINDD_PRIV_PIPE_DIR" }, diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c index b36f79fb3c..8e29b7d584 100644 --- a/source3/winbindd/winbindd_cm.c +++ b/source3/winbindd/winbindd_cm.c @@ -1419,6 +1419,88 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx, goto again; } +static char *current_dc_key(TALLOC_CTX *mem_ctx, const char *domain_name) +{ + return talloc_asprintf_strupper_m(mem_ctx, "CURRENT_DCNAME/%s", + domain_name); +} + +static void store_current_dc_in_gencache(const char *domain_name, + const char *dc_name, + struct cli_state *cli) +{ + char addr[INET6_ADDRSTRLEN]; + char *key, *value; + + if (cli == NULL) { + return; + } + if (cli->fd == -1) { + return; + } + get_peer_addr(cli->fd, addr, sizeof(addr)); + + key = current_dc_key(talloc_tos(), domain_name); + if (key == NULL) { + goto done; + } + + value = talloc_asprintf(talloc_tos(), "%s %s", addr, dc_name); + if (value == NULL) { + goto done; + } + + gencache_set(key, value, 0x7ffffffff); +done: + TALLOC_FREE(value); + TALLOC_FREE(key); +} + +bool fetch_current_dc_from_gencache(TALLOC_CTX *mem_ctx, + const char *domain_name, + char **p_dc_name, char **p_dc_ip) +{ + char *key, *value, *p; + bool ret = false; + char *dc_name = NULL; + char *dc_ip = NULL; + + key = current_dc_key(talloc_tos(), domain_name); + if (key == NULL) { + goto done; + } + if (!gencache_get(key, &value, NULL)) { + goto done; + } + p = strchr(value, ' '); + if (p == NULL) { + goto done; + } + dc_ip = talloc_strndup(mem_ctx, value, p - value); + if (dc_ip == NULL) { + goto done; + } + dc_name = talloc_strdup(mem_ctx, p+1); + if (dc_name == NULL) { + goto done; + } + + if (p_dc_ip != NULL) { + *p_dc_ip = dc_ip; + dc_ip = NULL; + } + if (p_dc_name != NULL) { + *p_dc_name = dc_name; + dc_name = NULL; + } + ret = true; +done: + TALLOC_FREE(dc_name); + TALLOC_FREE(dc_ip); + TALLOC_FREE(key); + return ret; +} + static NTSTATUS cm_open_connection(struct winbindd_domain *domain, struct winbindd_cm_conn *new_conn) { @@ -1520,6 +1602,17 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain, set_global_winbindd_state_online(); } set_domain_online(domain); + + /* + * Much as I hate global state, this seems to be the point + * where we can be certain that we have a proper connection to + * a DC. wbinfo --dc-info needs that information, store it in + * gencache with a looong timeout. This will need revisiting + * once we start to connect to multiple DCs, wbcDcInfo is + * already prepared for that. + */ + store_current_dc_in_gencache(domain->name, domain->dcname, + new_conn->cli); } else { /* Ensure we setup the retry handler. */ set_domain_offline(domain); diff --git a/source3/winbindd/winbindd_misc.c b/source3/winbindd/winbindd_misc.c index caf213b551..42ecea2aad 100644 --- a/source3/winbindd/winbindd_misc.c +++ b/source3/winbindd/winbindd_misc.c @@ -314,6 +314,56 @@ static void domain_info_done(struct tevent_req *req) request_ok(state->cli); } +void winbindd_dc_info(struct winbindd_cli_state *cli) +{ + struct winbindd_domain *domain; + char *dc_name, *dc_ip; + + cli->request->domain_name[sizeof(cli->request->domain_name-1)] = '\0'; + + DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)cli->pid, + cli->request->domain_name)); + + if (cli->request->domain_name[0] != '\0') { + domain = find_domain_from_name_noinit( + cli->request->domain_name); + DEBUG(10, ("Could not find domain %s\n", + cli->request->domain_name)); + if (domain == NULL) { + request_error(cli); + return; + } + } else { + domain = find_our_domain(); + } + + if (!fetch_current_dc_from_gencache( + talloc_tos(), domain->name, &dc_name, &dc_ip)) { + DEBUG(10, ("fetch_current_dc_from_gencache(%s) failed\n", + domain->name)); + request_error(cli); + return; + } + + cli->response->data.num_entries = 1; + cli->response->extra_data.data = talloc_asprintf( + cli->mem_ctx, "%s\n%s\n", dc_name, dc_ip); + + TALLOC_FREE(dc_name); + TALLOC_FREE(dc_ip); + + if (cli->response->extra_data.data == NULL) { + request_error(cli); + return; + } + + /* must add one to length to copy the 0 for string termination */ + cli->response->length += + strlen((char *)cli->response->extra_data.data) + 1; + + request_ok(cli); +} + /* List various tidbits of information */ void winbindd_info(struct winbindd_cli_state *state) diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h index dc563c9dac..5cb6c4c155 100644 --- a/source3/winbindd/winbindd_proto.h +++ b/source3/winbindd/winbindd_proto.h @@ -170,6 +170,9 @@ NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain, struct rpc_pipe_client **cli); NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain, struct rpc_pipe_client **cli); +bool fetch_current_dc_from_gencache(TALLOC_CTX *mem_ctx, + const char *domain_name, + char **p_dc_name, char **p_dc_ip); /* The following definitions come from winbindd/winbindd_cred_cache.c */ @@ -322,6 +325,7 @@ enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain * struct winbindd_cli_state *state); void winbindd_show_sequence(struct winbindd_cli_state *state); void winbindd_domain_info(struct winbindd_cli_state *state); +void winbindd_dc_info(struct winbindd_cli_state *state); void winbindd_ping(struct winbindd_cli_state *state); void winbindd_info(struct winbindd_cli_state *state); void winbindd_interface_version(struct winbindd_cli_state *state); |