s3: Add wbinfo --dc-info
authorVolker Lendecke <vl@samba.org>
Mon, 10 Jan 2011 16:25:00 +0000 (17:25 +0100)
committerChristian Ambach <christian.ambach@de.ibm.com>
Mon, 17 Jan 2011 13:18:16 +0000 (14:18 +0100)
nsswitch/libwbclient/wbc_util.c
nsswitch/libwbclient/wbclient.h
nsswitch/wbinfo.c
nsswitch/winbind_struct_protocol.h
source3/winbindd/winbindd.c
source3/winbindd/winbindd_cm.c
source3/winbindd/winbindd_misc.c
source3/winbindd/winbindd_proto.h

index c39023f15e29778fba2335fdc924c200f5652a48..2065f4be0535d14458a56c718f65e66910d651d8 100644 (file)
@@ -179,6 +179,94 @@ 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 = talloc_zero_array(NULL, const char *,
+                                 response.data.num_entries);
+       BAIL_ON_PTR_ERROR(names, wbc_status);
+
+       ips = talloc_zero_array(NULL, const char *,
+                               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] = talloc_strndup(names, 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] = talloc_strndup(ips, 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)
index 4970d1f2100badd085c7a327e17181f424e729b5..c41809b8844c94cfe0134e393caef01aa296ebb3 100644 (file)
@@ -188,7 +188,6 @@ struct wbcDomainInfo {
 #define WBC_DOMINFO_TRUSTTYPE_IN_FOREST  0x00000002
 #define WBC_DOMINFO_TRUSTTYPE_EXTERNAL   0x00000003
 
-
 /**
  * @brief Auth User Parameters
  **/
@@ -966,6 +965,20 @@ wbcErr wbcGetGroups(const char *account,
 wbcErr wbcDomainInfo(const char *domain,
                     struct wbcDomainInfo **info);
 
+/**
+ * @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
  *
index 6195b751cbed627550eaf213b20424cbcb29f448..36ec75681b9e509755d7db0195d7f596312c9b5e 100644 (file)
@@ -695,6 +695,31 @@ static bool wbinfo_dsgetdcname(const char *domain_name, uint32_t flags)
        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;
+}
+
 /* Check trust account password */
 
 static bool wbinfo_check_secret(void)
@@ -1663,6 +1688,7 @@ enum {
        OPT_SEQUENCE,
        OPT_GETDCNAME,
        OPT_DSGETDCNAME,
+       OPT_DC_INFO,
        OPT_USERDOMGROUPS,
        OPT_SIDALIASES,
        OPT_USERSIDS,
@@ -1749,6 +1775,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" },
@@ -2101,6 +2129,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_int(true);
                        if ( !sep ) {
index 0fae4d40aaad7bd9b1fb5736ae902d7641f808b0..a259b77367e0c6e0ff49d338ce58ea05c24affa4 100644 (file)
@@ -42,8 +42,9 @@
 /* Update this when you change the interface.
  * 21: added WINBINDD_GETPWSID
  *     added WINBINDD_GETSIDALIASES
+ * 22: added WINBINDD_DC_INFO
  */
-#define WINBIND_INTERFACE_VERSION 21
+#define WINBIND_INTERFACE_VERSION 22
 
 /* 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
@@ -119,6 +120,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 */
 
index a032436a0172e375d2208626a9aab1a86967391d..bbd602ee8c8b853fee86e6f6efae6a1c46a64157 100644 (file)
@@ -498,6 +498,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" },
index 5a6f9e2f6b951f633466e649f6003977685aff7a..8c77adf854bef01ce45c80f3c3142440b3e645ff 100644 (file)
@@ -1380,6 +1380,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)
 {
@@ -1480,6 +1562,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);
index 463e8375c84e5952aa35e06a3f0d2ab90f79b2c4..c3fec223ce0c6ac92e14bf16b608a4053b555ba3 100644 (file)
@@ -790,6 +790,59 @@ void winbindd_ping(struct winbindd_cli_state *state)
        request_ok(state);
 }
 
+void winbindd_dc_info(struct winbindd_cli_state *cli)
+{
+       struct winbindd_domain *domain;
+       char *dc_name, *dc_ip;
+       char *extra;
+       bool ret;
+
+       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;
+       }
+
+       ret = (asprintf(&extra, "%s\n%s\n", dc_name, dc_ip) > 0);
+
+       TALLOC_FREE(dc_name);
+       TALLOC_FREE(dc_ip);
+
+       if (!ret) {
+               request_error(cli);
+               return;
+       }
+
+       cli->response.data.num_entries = 1;
+       cli->response.extra_data.data = extra;
+
+       /* 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)
index d666639420b5bd0fc508da067e236d7d67373a2b..2384bf2d93ab9e08d130bb3b3181a74205192866 100644 (file)
@@ -229,6 +229,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  */
 
@@ -426,6 +429,7 @@ void winbindd_show_sequence(struct winbindd_cli_state *state);
 enum winbindd_result winbindd_dual_show_sequence(struct winbindd_domain *domain,
                                                 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);