s3:libsmb: get rid of cli_state_remote_name
[samba.git] / source3 / winbindd / winbindd_cm.c
index d9b431a5011748742d1512bbea4cc8c6912d87b3..c515ace34776aa477b1f6c59a5c6a0b85336aeb3 100644 (file)
 #include "includes.h"
 #include "winbindd.h"
 #include "../libcli/auth/libcli_auth.h"
-#include "../librpc/gen_ndr/cli_netlogon.h"
-#include "../librpc/gen_ndr/cli_samr.h"
-#include "../librpc/gen_ndr/cli_lsa.h"
-#include "../librpc/gen_ndr/cli_dssetup.h"
+#include "../librpc/gen_ndr/ndr_netlogon_c.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/cli_netlogon.h"
+#include "../librpc/gen_ndr/ndr_samr_c.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "../librpc/gen_ndr/ndr_dssetup_c.h"
 #include "libads/sitename_cache.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clidgram.h"
+#include "ads.h"
+#include "secrets.h"
+#include "../libcli/security/security.h"
+#include "passdb.h"
+#include "messages.h"
+#include "auth/gensec/gensec.h"
+#include "../libcli/smb/smbXcli_base.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
@@ -177,11 +189,9 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
        struct dc_name_ip *dcs = NULL;
        int num_dcs = 0;
        TALLOC_CTX *mem_ctx = NULL;
-       pid_t parent_pid = sys_getpid();
+       pid_t parent_pid = getpid();
        char *lfile = NULL;
-
-       /* Stop zombies */
-       CatchChild();
+       NTSTATUS status;
 
        if (domain->dc_probe_pid != (pid_t)-1) {
                /*
@@ -197,7 +207,7 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
                domain->dc_probe_pid = (pid_t)-1;
        }
 
-       domain->dc_probe_pid = sys_fork();
+       domain->dc_probe_pid = fork();
 
        if (domain->dc_probe_pid == (pid_t)-1) {
                DEBUG(0, ("fork_child_dc_connect: Could not fork: %s\n", strerror(errno)));
@@ -226,7 +236,10 @@ static bool fork_child_dc_connect(struct winbindd_domain *domain)
                }
        }
 
-       if (!winbindd_reinit_after_fork(lfile)) {
+       status = winbindd_reinit_after_fork(NULL, lfile);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("winbindd_reinit_after_fork failed: %s\n",
+                         nt_errstr(status)));
                messaging_send_buf(winbind_messaging_context(),
                                   pid_to_procid(parent_pid),
                                   MSG_WINBIND_FAILED_TO_GO_ONLINE,
@@ -288,7 +301,7 @@ static void check_domain_online_handler(struct event_context *ctx,
 
        /* Are we still in "startup" mode ? */
 
-       if (domain->startup && (now.tv_sec > domain->startup_time + 30)) {
+       if (domain->startup && (time_mono(NULL) > domain->startup_time + 30)) {
                /* No longer in "startup" mode. */
                DEBUG(10,("check_domain_online_handler: domain %s no longer in 'startup' mode.\n",
                        domain->name ));
@@ -505,7 +518,7 @@ void set_domain_online_request(struct winbindd_domain *domain)
        GetTimeOfDay(&tev);
 
        /* Go into "startup" mode again. */
-       domain->startup_time = tev.tv_sec;
+       domain->startup_time = time_mono(NULL);
        domain->startup = True;
 
        tev.tv_sec += 5;
@@ -538,9 +551,10 @@ void set_domain_online_request(struct winbindd_domain *domain)
  Add -ve connection cache entries for domain and realm.
 ****************************************************************/
 
-void winbind_add_failed_connection_entry(const struct winbindd_domain *domain,
-                                       const char *server,
-                                       NTSTATUS result)
+static void winbind_add_failed_connection_entry(
+       const struct winbindd_domain *domain,
+       const char *server,
+       NTSTATUS result)
 {
        add_failed_connection_entry(domain->name, server, result);
        /* If this was the saf name for the last thing we talked to,
@@ -598,6 +612,7 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
        unsigned int orig_timeout;
        const char *tmp = NULL;
        const char *p;
+       struct dcerpc_binding_handle *b;
 
        /* Hmmmm. We can only open one connection to the NETLOGON pipe at the
         * moment.... */
@@ -622,6 +637,8 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
                return False;
        }
 
+       b = netlogon_pipe->binding_handle;
+
        /* This call can take a long time - allow the server to time out.
           35 seconds should do it. */
 
@@ -630,7 +647,7 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
        if (our_domain->active_directory) {
                struct netr_DsRGetDCNameInfo *domain_info = NULL;
 
-               result = rpccli_netr_DsRGetDCName(netlogon_pipe,
+               result = dcerpc_netr_DsRGetDCName(b,
                                                  mem_ctx,
                                                  our_domain->dcname,
                                                  domain->name,
@@ -657,7 +674,7 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
                        }
                }
        } else {
-               result = rpccli_netr_GetAnyDCName(netlogon_pipe, mem_ctx,
+               result = dcerpc_netr_GetAnyDCName(b, mem_ctx,
                                                  our_domain->dcname,
                                                  domain->name,
                                                  &tmp,
@@ -668,27 +685,27 @@ static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
        rpccli_set_timeout(netlogon_pipe, orig_timeout);
 
        if (!NT_STATUS_IS_OK(result)) {
-               DEBUG(10,("rpccli_netr_GetAnyDCName failed: %s\n",
+               DEBUG(10,("dcerpc_netr_GetAnyDCName failed: %s\n",
                        nt_errstr(result)));
                talloc_destroy(mem_ctx);
                return false;
        }
 
        if (!W_ERROR_IS_OK(werr)) {
-               DEBUG(10,("rpccli_netr_GetAnyDCName failed: %s\n",
+               DEBUG(10,("dcerpc_netr_GetAnyDCName failed: %s\n",
                           win_errstr(werr)));
                talloc_destroy(mem_ctx);
                return false;
        }
 
-       /* rpccli_netr_GetAnyDCName gives us a name with \\ */
+       /* dcerpc_netr_GetAnyDCName gives us a name with \\ */
        p = strip_hostname(tmp);
 
        fstrcpy(dcname, p);
 
        talloc_destroy(mem_ctx);
 
-       DEBUG(10,("rpccli_netr_GetAnyDCName returned %s\n", dcname));
+       DEBUG(10,("dcerpc_netr_GetAnyDCName returned %s\n", dcname));
 
        if (!resolve_name(dcname, dc_ss, 0x20, true)) {
                return False;
@@ -766,23 +783,21 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
                                      struct cli_state **cli,
                                      bool *retry)
 {
+       bool try_spnego = false;
+       bool try_ipc_auth = false;
        char *machine_password = NULL;
        char *machine_krb5_principal = NULL;
        char *machine_account = NULL;
        char *ipc_username = NULL;
        char *ipc_domain = NULL;
        char *ipc_password = NULL;
+       int flags = 0;
+       uint16_t sec_mode = 0;
 
        struct named_mutex *mutex;
 
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
 
-       struct sockaddr peeraddr;
-       socklen_t peeraddr_len;
-
-       struct sockaddr_in *peeraddr_in =
-               (struct sockaddr_in *)(void *)&peeraddr;
-
        DEBUG(10,("cm_prepare_connection: connecting to DC %s for domain %s\n",
                controller, domain->name ));
 
@@ -791,62 +806,42 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
        mutex = grab_named_mutex(talloc_tos(), controller,
                                 WINBIND_SERVER_MUTEX_WAIT_TIME);
        if (mutex == NULL) {
+               close(sockfd);
                DEBUG(0,("cm_prepare_connection: mutex grab failed for %s\n",
                         controller));
                result = NT_STATUS_POSSIBLE_DEADLOCK;
                goto done;
        }
 
-       if ((*cli = cli_initialise()) == NULL) {
+       flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+
+       *cli = cli_state_create(NULL, sockfd,
+                               controller, domain->alt_name,
+                               SMB_SIGNING_DEFAULT, flags);
+       if (*cli == NULL) {
+               close(sockfd);
                DEBUG(1, ("Could not cli_initialize\n"));
                result = NT_STATUS_NO_MEMORY;
                goto done;
        }
 
-       (*cli)->timeout = 10000;        /* 10 seconds */
-       (*cli)->fd = sockfd;
-       fstrcpy((*cli)->desthost, controller);
-       (*cli)->use_kerberos = True;
-
-       peeraddr_len = sizeof(peeraddr);
-
-       if ((getpeername((*cli)->fd, &peeraddr, &peeraddr_len) != 0) ||
-           (peeraddr_len != sizeof(struct sockaddr_in)) ||
-           (peeraddr_in->sin_family != PF_INET))
-       {
-               DEBUG(0,("cm_prepare_connection: %s\n", strerror(errno)));
-               result = NT_STATUS_UNSUCCESSFUL;
-               goto done;
-       }
-
-       if (ntohs(peeraddr_in->sin_port) == 139) {
-               struct nmb_name calling;
-               struct nmb_name called;
-
-               make_nmb_name(&calling, global_myname(), 0x0);
-               make_nmb_name(&called, "*SMBSERVER", 0x20);
-
-               if (!cli_session_request(*cli, &calling, &called)) {
-                       DEBUG(8, ("cli_session_request failed for %s\n",
-                                 controller));
-                       result = NT_STATUS_UNSUCCESSFUL;
-                       goto done;
-               }
-       }
+       cli_set_timeout(*cli, 10000); /* 10 seconds */
 
-       result = cli_negprot(*cli);
+       result = cli_negprot(*cli, PROTOCOL_SMB2_02);
 
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(1, ("cli_negprot failed: %s\n", nt_errstr(result)));
                goto done;
        }
 
-       if (!is_dc_trusted_domain_situation(domain->name) &&
-           (*cli)->protocol >= PROTOCOL_NT1 &&
-           (*cli)->capabilities & CAP_EXTENDED_SECURITY)
-       {
-               ADS_STATUS ads_status;
+       if (cli_state_protocol(*cli) >= PROTOCOL_NT1 &&
+           cli_state_capabilities(*cli) & CAP_EXTENDED_SECURITY) {
+               try_spnego = true;
+       } else if (cli_state_protocol(*cli) >= PROTOCOL_SMB2_02) {
+               try_spnego = true;
+       }
 
+       if (!is_dc_trusted_domain_situation(domain->name) && try_spnego) {
                result = get_trust_creds(domain, &machine_password,
                                         &machine_account,
                                         &machine_krb5_principal);
@@ -860,23 +855,24 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
 
                        (*cli)->use_kerberos = True;
                        DEBUG(5, ("connecting to %s from %s with kerberos principal "
-                                 "[%s] and realm [%s]\n", controller, global_myname(),
+                                 "[%s] and realm [%s]\n", controller, lp_netbios_name(),
                                  machine_krb5_principal, domain->alt_name));
 
                        winbindd_set_locator_kdc_envs(domain);
 
-                       ads_status = cli_session_setup_spnego(*cli,
-                                                             machine_krb5_principal, 
-                                                             machine_password,
-                                                             lp_workgroup(),
-                                                             domain->alt_name);
+                       result = cli_session_setup(*cli,
+                                                  machine_krb5_principal,
+                                                  machine_password,
+                                                  strlen(machine_password)+1,
+                                                  machine_password,
+                                                  strlen(machine_password)+1,
+                                                  lp_workgroup());
 
-                       if (!ADS_ERR_OK(ads_status)) {
+                       if (!NT_STATUS_IS_OK(result)) {
                                DEBUG(4,("failed kerberos session setup with %s\n",
-                                        ads_errstr(ads_status)));
+                                       nt_errstr(result)));
                        }
 
-                       result = ads_ntstatus(ads_status);
                        if (NT_STATUS_IS_OK(result)) {
                                /* Ensure creds are stored for NTLMSSP authenticated pipe access. */
                                result = cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password);
@@ -891,20 +887,21 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
                (*cli)->use_kerberos = False;
 
                DEBUG(5, ("connecting to %s from %s with username "
-                         "[%s]\\[%s]\n",  controller, global_myname(),
+                         "[%s]\\[%s]\n",  controller, lp_netbios_name(),
                          lp_workgroup(), machine_account));
 
-               ads_status = cli_session_setup_spnego(*cli,
-                                                     machine_account, 
-                                                     machine_password, 
-                                                     lp_workgroup(),
-                                                     NULL);
-               if (!ADS_ERR_OK(ads_status)) {
+               result = cli_session_setup(*cli,
+                                          machine_account,
+                                          machine_password,
+                                          strlen(machine_password)+1,
+                                          machine_password,
+                                          strlen(machine_password)+1,
+                                          lp_workgroup());
+               if (!NT_STATUS_IS_OK(result)) {
                        DEBUG(4, ("authenticated session setup failed with %s\n",
-                               ads_errstr(ads_status)));
+                               nt_errstr(result)));
                }
 
-               result = ads_ntstatus(ads_status);
                if (NT_STATUS_IS_OK(result)) {
                        /* Ensure creds are stored for NTLMSSP authenticated pipe access. */
                        result = cli_init_creds(*cli, machine_account, lp_workgroup(), machine_password);
@@ -921,13 +918,21 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
 
        cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
 
-       if ((((*cli)->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) != 0) &&
-           (strlen(ipc_username) > 0)) {
+       sec_mode = cli_state_security_mode(*cli);
+
+       try_ipc_auth = false;
+       if (try_spnego) {
+               try_ipc_auth = true;
+       } else if (sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+               try_ipc_auth = true;
+       }
+
+       if (try_ipc_auth && (strlen(ipc_username) > 0)) {
 
                /* Only try authenticated if we have a username */
 
                DEBUG(5, ("connecting to %s from %s with username "
-                         "[%s]\\[%s]\n",  controller, global_myname(),
+                         "[%s]\\[%s]\n",  controller, lp_netbios_name(),
                          ipc_domain, ipc_username));
 
                if (NT_STATUS_IS_OK(cli_session_setup(
@@ -954,8 +959,8 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
                "connection for DC %s\n",
                controller ));
 
-       if (NT_STATUS_IS_OK(cli_session_setup(*cli, "", NULL, 0,
-                                             NULL, 0, ""))) {
+       result = cli_session_setup(*cli, "", NULL, 0, NULL, 0, "");
+       if (NT_STATUS_IS_OK(result)) {
                DEBUG(5, ("Connected anonymously\n"));
                result = cli_init_creds(*cli, "", "", "");
                if (!NT_STATUS_IS_OK(result)) {
@@ -964,27 +969,21 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain,
                goto session_setup_done;
        }
 
-       result = cli_nt_error(*cli);
-
-       if (NT_STATUS_IS_OK(result))
-               result = NT_STATUS_UNSUCCESSFUL;
-
        /* We can't session setup */
-
        goto done;
 
  session_setup_done:
 
        /* cache the server name for later connections */
 
-       saf_store( domain->name, (*cli)->desthost );
+       saf_store(domain->name, controller);
        if (domain->alt_name && (*cli)->use_kerberos) {
-               saf_store( domain->alt_name, (*cli)->desthost );
+               saf_store(domain->alt_name, controller);
        }
 
        winbindd_set_locator_kdc_envs(domain);
 
-       result = cli_tcon_andx(*cli, "IPC$", "IPC", "", 0);
+       result = cli_tree_connect(*cli, "IPC$", "IPC", "", 0);
 
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
@@ -1057,7 +1056,7 @@ static bool add_one_dc_unique(TALLOC_CTX *mem_ctx, const char *domain_name,
                            (struct sockaddr *)(void *)pss))
                        return False;
 
-       *dcs = TALLOC_REALLOC_ARRAY(mem_ctx, *dcs, struct dc_name_ip, (*num)+1);
+       *dcs = talloc_realloc(mem_ctx, *dcs, struct dc_name_ip, (*num)+1);
 
        if (*dcs == NULL)
                return False;
@@ -1072,7 +1071,7 @@ static bool add_sockaddr_to_array(TALLOC_CTX *mem_ctx,
                                  struct sockaddr_storage *pss, uint16 port,
                                  struct sockaddr_storage **addrs, int *num)
 {
-       *addrs = TALLOC_REALLOC_ARRAY(mem_ctx, *addrs, struct sockaddr_storage, (*num)+1);
+       *addrs = talloc_realloc(mem_ctx, *addrs, struct sockaddr_storage, (*num)+1);
 
        if (*addrs == NULL) {
                *num = 0;
@@ -1097,11 +1096,13 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
 {
        struct ip_service ip_list;
        uint32_t nt_version = NETLOGON_NT_VERSION_1;
+       NTSTATUS status;
+       const char *dc_name;
 
        ip_list.ss = *pss;
        ip_list.port = 0;
 
-#ifdef WITH_ADS
+#ifdef HAVE_ADS
        /* For active directory servers, try to get the ldap server name.
           None of these failures should be considered critical for now */
 
@@ -1134,7 +1135,8 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
                                        create_local_private_krb5_conf_for_domain(domain->alt_name,
                                                                        domain->name,
                                                                        sitename,
-                                                                       pss);
+                                                                       pss,
+                                                                       name);
 
                                        SAFE_FREE(sitename);
                                } else {
@@ -1142,7 +1144,8 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
                                        create_local_private_krb5_conf_for_domain(domain->alt_name,
                                                                        domain->name,
                                                                        NULL,
-                                                                       pss);
+                                                                       pss,
+                                                                       name);
                                }
                                winbindd_set_locator_kdc_envs(domain);
 
@@ -1156,27 +1159,17 @@ static bool dcip_to_name(TALLOC_CTX *mem_ctx,
                }
 
                ads_destroy( &ads );
+               return false;
        }
 #endif
 
-       /* try GETDC requests next */
-
-       if (send_getdc_request(mem_ctx, winbind_messaging_context(),
-                              pss, domain->name, &domain->sid,
-                              nt_version)) {
-               const char *dc_name = NULL;
-               int i;
-               smb_msleep(100);
-               for (i=0; i<5; i++) {
-                       if (receive_getdc_response(mem_ctx, pss, domain->name,
-                                                  &nt_version,
-                                                  &dc_name, NULL)) {
-                               fstrcpy(name, dc_name);
-                               namecache_store(name, 0x20, 1, &ip_list);
-                               return True;
-                       }
-                       smb_msleep(500);
-               }
+       status = nbt_getdc(winbind_messaging_context(), 10, pss, domain->name,
+                          &domain->sid, nt_version, mem_ctx, &nt_version,
+                          &dc_name, NULL);
+       if (NT_STATUS_IS_OK(status)) {
+               fstrcpy(name, dc_name);
+               namecache_store(name, 0x20, 1, &ip_list);
+               return True;
        }
 
        /* try node status request */
@@ -1336,7 +1329,10 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx,
        struct sockaddr_storage *addrs = NULL;
        int num_addrs = 0;
 
-       int i, fd_index;
+       int i;
+       size_t fd_index;
+
+       NTSTATUS status;
 
        *fd = -1;
 
@@ -1350,16 +1346,7 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx,
                                    &dcnames, &num_dcnames)) {
                        return False;
                }
-               if (!add_sockaddr_to_array(mem_ctx, &dcs[i].ss, 445,
-                                     &addrs, &num_addrs)) {
-                       return False;
-               }
-
-               if (!add_string_to_array(mem_ctx, dcs[i].name,
-                                   &dcnames, &num_dcnames)) {
-                       return False;
-               }
-               if (!add_sockaddr_to_array(mem_ctx, &dcs[i].ss, 139,
+               if (!add_sockaddr_to_array(mem_ctx, &dcs[i].ss, TCP_SMB_PORT,
                                      &addrs, &num_addrs)) {
                        return False;
                }
@@ -1371,14 +1358,15 @@ static bool find_new_dc(TALLOC_CTX *mem_ctx,
        if ((addrs == NULL) || (dcnames == NULL))
                return False;
 
-       /* 5 second timeout. */
-       if (!open_any_socket_out(addrs, num_addrs, 5000, &fd_index, fd) ) {
+       status = smbsock_any_connect(addrs, dcnames, NULL, NULL, NULL,
+                                    num_addrs, 0, 10, fd, &fd_index, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
                for (i=0; i<num_dcs; i++) {
                        char ab[INET6_ADDRSTRLEN];
                        print_sockaddr(ab, sizeof(ab), &dcs[i].ss);
-                       DEBUG(10, ("find_new_dc: open_any_socket_out failed for "
+                       DEBUG(10, ("find_new_dc: smbsock_any_connect failed for "
                                "domain %s address %s. Error was %s\n",
-                               domain->name, ab, strerror(errno) ));
+                                  domain->name, ab, nt_errstr(status) ));
                        winbind_add_failed_connection_entry(domain,
                                dcs[i].name, NT_STATUS_UNSUCCESSFUL);
                }
@@ -1418,6 +1406,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 = NULL;
+       char *value = NULL;
+
+       if (!cli_state_is_connected(cli)) {
+               return;
+       }
+
+       print_sockaddr(addr, sizeof(addr),
+                      smbXcli_conn_remote_sockaddr(cli->conn));
+
+       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, 0x7fffffff);
+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)
 {
@@ -1433,7 +1503,7 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
        }
 
        /* we have to check the server affinity cache here since 
-          later we selecte a DC based on response time and not preference */
+          later we select a DC based on response time and not preference */
 
        /* Check the negative connection cache
           before talking to it. It going down may have
@@ -1454,7 +1524,7 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                                return NT_STATUS_UNSUCCESSFUL;
                        }
                        if (dcip_to_name(mem_ctx, domain, &ss, saf_name )) {
-                               fstrcpy( domain->dcname, saf_name );
+                               strlcpy(domain->dcname, saf_name, sizeof(domain->dcname));
                        } else {
                                winbind_add_failed_connection_entry(
                                        domain, saf_servername,
@@ -1480,23 +1550,12 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
                        && NT_STATUS_IS_OK(check_negative_conn_cache( domain->name, domain->dcname))
                        && (resolve_name(domain->dcname, &domain->dcaddr, 0x20, true)))
                {
-                       struct sockaddr_storage *addrs = NULL;
-                       int num_addrs = 0;
-                       int dummy = 0;
+                       NTSTATUS status;
 
-                       if (!add_sockaddr_to_array(mem_ctx, &domain->dcaddr, 445, &addrs, &num_addrs)) {
-                               set_domain_offline(domain);
-                               talloc_destroy(mem_ctx);
-                               return NT_STATUS_NO_MEMORY;
-                       }
-                       if (!add_sockaddr_to_array(mem_ctx, &domain->dcaddr, 139, &addrs, &num_addrs)) {
-                               set_domain_offline(domain);
-                               talloc_destroy(mem_ctx);
-                               return NT_STATUS_NO_MEMORY;
-                       }
-
-                       /* 5 second timeout. */
-                       if (!open_any_socket_out(addrs, num_addrs, 5000, &dummy, &fd)) {
+                       status = smbsock_connect(&domain->dcaddr, 0,
+                                                NULL, -1, NULL, -1,
+                                                &fd, NULL, 10);
+                       if (!NT_STATUS_IS_OK(status)) {
                                fd = -1;
                        }
                }
@@ -1530,6 +1589,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);
@@ -1543,6 +1613,8 @@ static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
 
 void invalidate_cm_connection(struct winbindd_cm_conn *conn)
 {
+       NTSTATUS result;
+
        /* We're closing down a possibly dead
           connection. Don't have impossibly long (10s) timeouts. */
 
@@ -1551,6 +1623,12 @@ void invalidate_cm_connection(struct winbindd_cm_conn *conn)
        }
 
        if (conn->samr_pipe != NULL) {
+               if (is_valid_policy_hnd(&conn->sam_connect_handle)) {
+                       dcerpc_samr_Close(conn->samr_pipe->binding_handle,
+                                         talloc_tos(),
+                                         &conn->sam_connect_handle,
+                                         &result);
+               }
                TALLOC_FREE(conn->samr_pipe);
                /* Ok, it must be dead. Drop timeout to 0.5 sec. */
                if (conn->cli) {
@@ -1559,6 +1637,12 @@ void invalidate_cm_connection(struct winbindd_cm_conn *conn)
        }
 
        if (conn->lsa_pipe != NULL) {
+               if (is_valid_policy_hnd(&conn->lsa_policy)) {
+                       dcerpc_lsa_Close(conn->lsa_pipe->binding_handle,
+                                        talloc_tos(),
+                                        &conn->lsa_policy,
+                                        &result);
+               }
                TALLOC_FREE(conn->lsa_pipe);
                /* Ok, it must be dead. Drop timeout to 0.5 sec. */
                if (conn->cli) {
@@ -1567,6 +1651,12 @@ void invalidate_cm_connection(struct winbindd_cm_conn *conn)
        }
 
        if (conn->lsa_pipe_tcp != NULL) {
+               if (is_valid_policy_hnd(&conn->lsa_policy)) {
+                       dcerpc_lsa_Close(conn->lsa_pipe_tcp->binding_handle,
+                                        talloc_tos(),
+                                        &conn->lsa_policy,
+                                        &result);
+               }
                TALLOC_FREE(conn->lsa_pipe_tcp);
                /* Ok, it must be dead. Drop timeout to 0.5 sec. */
                if (conn->cli) {
@@ -1592,16 +1682,28 @@ void invalidate_cm_connection(struct winbindd_cm_conn *conn)
 void close_conns_after_fork(void)
 {
        struct winbindd_domain *domain;
+       struct winbindd_cli_state *cli_state;
 
        for (domain = domain_list(); domain; domain = domain->next) {
-               if (domain->conn.cli == NULL)
-                       continue;
+               /*
+                * first close the low level SMB TCP connection
+                * so that we don't generate any SMBclose
+                * requests in invalidate_cm_connection()
+                */
+               if (cli_state_is_connected(domain->conn.cli)) {
+                       cli_state_disconnect(domain->conn.cli);
+               }
 
-               if (domain->conn.cli->fd == -1)
-                       continue;
+               invalidate_cm_connection(&domain->conn);
+       }
 
-               close(domain->conn.cli->fd);
-               domain->conn.cli->fd = -1;
+       for (cli_state = winbindd_client_list();
+            cli_state != NULL;
+            cli_state = cli_state->next) {
+               if (cli_state->sock >= 0) {
+                       close(cli_state->sock);
+                       cli_state->sock = -1;
+               }
        }
 }
 
@@ -1632,7 +1734,13 @@ static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain)
        NTSTATUS result;
 
        /* Internal connections never use the network. */
-       if (domain->internal || !winbindd_can_contact_domain(domain)) {
+       if (domain->internal) {
+               domain->initialized = True;
+               return NT_STATUS_OK;
+       }
+
+       if (!winbindd_can_contact_domain(domain)) {
+               invalidate_cm_connection(&domain->conn);
                domain->initialized = True;
                return NT_STATUS_OK;
        }
@@ -1657,6 +1765,10 @@ static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain)
 
 NTSTATUS init_dc_connection(struct winbindd_domain *domain)
 {
+       if (domain->internal) {
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
        if (domain->initialized && !domain->online) {
                /* We check for online status elsewhere. */
                return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
@@ -1665,6 +1777,23 @@ NTSTATUS init_dc_connection(struct winbindd_domain *domain)
        return init_dc_connection_network(domain);
 }
 
+static NTSTATUS init_dc_connection_rpc(struct winbindd_domain *domain)
+{
+       NTSTATUS status;
+
+       status = init_dc_connection(domain);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (!domain->internal && domain->conn.cli == NULL) {
+               /* happens for trusted domains without inbound trust */
+               return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+       }
+
+       return NT_STATUS_OK;
+}
+
 /******************************************************************************
  Set the trust flags (direction and forest location) for a domain
 ******************************************************************************/
@@ -1673,6 +1802,7 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
 {
        struct winbindd_domain *our_domain;
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       WERROR werr;
        struct netr_DomainTrustList trusts;
        int i;
        uint32 flags = (NETR_TRUST_FLAG_IN_FOREST |
@@ -1680,6 +1810,7 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
                        NETR_TRUST_FLAG_INBOUND);
        struct rpc_pipe_client *cli;
        TALLOC_CTX *mem_ctx = NULL;
+       struct dcerpc_binding_handle *b;
 
        DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s\n", domain->name ));
 
@@ -1714,16 +1845,18 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
                return False;
        }
 
+       b = cli->binding_handle;
+
        if ( (mem_ctx = talloc_init("set_dc_type_and_flags_trustinfo")) == NULL ) {
                DEBUG(0,("set_dc_type_and_flags_trustinfo: talloc_init() failed!\n"));
                return False;
        }       
 
-       result = rpccli_netr_DsrEnumerateDomainTrusts(cli, mem_ctx,
+       result = dcerpc_netr_DsrEnumerateDomainTrusts(b, mem_ctx,
                                                      cli->desthost,
                                                      flags,
                                                      &trusts,
-                                                     NULL);
+                                                     &werr);
        if (!NT_STATUS_IS_OK(result)) {
                DEBUG(0,("set_dc_type_and_flags_trustinfo: "
                        "failed to query trusted domain list: %s\n",
@@ -1731,6 +1864,13 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
                talloc_destroy(mem_ctx);
                return false;
        }
+       if (!W_ERROR_IS_OK(werr)) {
+               DEBUG(0,("set_dc_type_and_flags_trustinfo: "
+                       "failed to query trusted domain list: %s\n",
+                       win_errstr(werr)));
+               talloc_destroy(mem_ctx);
+               return false;
+       }
 
        /* Now find the domain name and get the flags */
 
@@ -1757,6 +1897,8 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
                                 "running active directory.\n", domain->name, 
                                 domain->active_directory ? "" : "NOT "));
 
+                       domain->can_do_ncacn_ip_tcp = domain->active_directory;
+                       domain->can_do_validation6 = domain->active_directory;
 
                        domain->initialized = True;
 
@@ -1779,7 +1921,7 @@ static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
 
 static void set_dc_type_and_flags_connect( struct winbindd_domain *domain )
 {
-       NTSTATUS                result;
+       NTSTATUS status, result;
        WERROR werr;
        TALLOC_CTX              *mem_ctx = NULL;
        struct rpc_pipe_client  *cli = NULL;
@@ -1800,14 +1942,14 @@ static void set_dc_type_and_flags_connect( struct winbindd_domain *domain )
 
        DEBUG(5, ("set_dc_type_and_flags_connect: domain %s\n", domain->name ));
 
-       result = cli_rpc_pipe_open_noauth(domain->conn.cli,
+       status = cli_rpc_pipe_open_noauth(domain->conn.cli,
                                          &ndr_table_dssetup.syntax_id,
                                          &cli);
 
-       if (!NT_STATUS_IS_OK(result)) {
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to "
                          "PI_DSSETUP on domain %s: (%s)\n",
-                         domain->name, nt_errstr(result)));
+                         domain->name, nt_errstr(status)));
 
                /* if this is just a non-AD domain we need to continue
                 * identifying so that we can in the end return with
@@ -1816,23 +1958,26 @@ static void set_dc_type_and_flags_connect( struct winbindd_domain *domain )
                goto no_dssetup;
        }
 
-       result = rpccli_dssetup_DsRoleGetPrimaryDomainInformation(cli, mem_ctx,
+       status = dcerpc_dssetup_DsRoleGetPrimaryDomainInformation(cli->binding_handle, mem_ctx,
                                                                  DS_ROLE_BASIC_INFORMATION,
                                                                  &info,
                                                                  &werr);
        TALLOC_FREE(cli);
 
-       if (!NT_STATUS_IS_OK(result)) {
+       if (NT_STATUS_IS_OK(status)) {
+               result = werror_to_ntstatus(werr);
+       }
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("set_dc_type_and_flags_connect: rpccli_ds_getprimarydominfo "
                          "on domain %s failed: (%s)\n",
-                         domain->name, nt_errstr(result)));
+                         domain->name, nt_errstr(status)));
 
                /* older samba3 DCs will return DCERPC_FAULT_OP_RNG_ERROR for
                 * every opcode on the DSSETUP pipe, continue with
                 * no_dssetup mode here as well to get domain->initialized
                 * set - gd */
 
-               if (NT_STATUS_V(result) == DCERPC_FAULT_OP_RNG_ERROR) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
                        goto no_dssetup;
                }
 
@@ -1848,31 +1993,32 @@ static void set_dc_type_and_flags_connect( struct winbindd_domain *domain )
        }
 
 no_dssetup:
-       result = cli_rpc_pipe_open_noauth(domain->conn.cli,
+       status = cli_rpc_pipe_open_noauth(domain->conn.cli,
                                          &ndr_table_lsarpc.syntax_id, &cli);
 
-       if (!NT_STATUS_IS_OK(result)) {
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to "
                          "PI_LSARPC on domain %s: (%s)\n",
-                         domain->name, nt_errstr(result)));
+                         domain->name, nt_errstr(status)));
                TALLOC_FREE(cli);
                TALLOC_FREE(mem_ctx);
                return;
        }
 
-       result = rpccli_lsa_open_policy2(cli, mem_ctx, True, 
+       status = rpccli_lsa_open_policy2(cli, mem_ctx, True,
                                         SEC_FLAG_MAXIMUM_ALLOWED, &pol);
 
-       if (NT_STATUS_IS_OK(result)) {
+       if (NT_STATUS_IS_OK(status)) {
                /* This particular query is exactly what Win2k clients use 
                   to determine that the DC is active directory */
-               result = rpccli_lsa_QueryInfoPolicy2(cli, mem_ctx,
+               status = dcerpc_lsa_QueryInfoPolicy2(cli->binding_handle, mem_ctx,
                                                     &pol,
                                                     LSA_POLICY_INFO_DNS,
-                                                    &lsa_info);
+                                                    &lsa_info,
+                                                    &result);
        }
 
-       if (NT_STATUS_IS_OK(result)) {
+       if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
                domain->active_directory = True;
 
                if (lsa_info->dns.name.string) {
@@ -1902,20 +2048,20 @@ no_dssetup:
        } else {
                domain->active_directory = False;
 
-               result = rpccli_lsa_open_policy(cli, mem_ctx, True, 
+               status = rpccli_lsa_open_policy(cli, mem_ctx, True,
                                                SEC_FLAG_MAXIMUM_ALLOWED,
                                                &pol);
 
-               if (!NT_STATUS_IS_OK(result)) {
+               if (!NT_STATUS_IS_OK(status)) {
                        goto done;
                }
 
-               result = rpccli_lsa_QueryInfoPolicy(cli, mem_ctx,
+               status = dcerpc_lsa_QueryInfoPolicy(cli->binding_handle, mem_ctx,
                                                    &pol,
                                                    LSA_POLICY_INFO_ACCOUNT_DOMAIN,
-                                                   &lsa_info);
-
-               if (NT_STATUS_IS_OK(result)) {
+                                                   &lsa_info,
+                                                   &result);
+               if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
 
                        if (lsa_info->account_domain.name.string) {
                                fstrcpy(domain->name,
@@ -1936,6 +2082,7 @@ done:
                  domain->name, domain->active_directory ? "" : "NOT "));
 
        domain->can_do_ncacn_ip_tcp = domain->active_directory;
+       domain->can_do_validation6 = domain->active_directory;
 
        TALLOC_FREE(cli);
 
@@ -1975,53 +2122,58 @@ static void set_dc_type_and_flags( struct winbindd_domain *domain )
 /**********************************************************************
 ***********************************************************************/
 
-static bool cm_get_schannel_creds(struct winbindd_domain *domain,
+static NTSTATUS cm_get_schannel_creds(struct winbindd_domain *domain,
                                   struct netlogon_creds_CredentialState **ppdc)
 {
-       NTSTATUS result;
+       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
        struct rpc_pipe_client *netlogon_pipe;
 
        if (lp_client_schannel() == False) {
-               return False;
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
        result = cm_connect_netlogon(domain, &netlogon_pipe);
        if (!NT_STATUS_IS_OK(result)) {
-               return False;
+               return result;
        }
 
        /* Return a pointer to the struct netlogon_creds_CredentialState from the
           netlogon pipe. */
 
        if (!domain->conn.netlogon_pipe->dc) {
-               return false;
+               return NT_STATUS_INTERNAL_ERROR; /* This shouldn't happen. */
        }
 
        *ppdc = domain->conn.netlogon_pipe->dc;
-       return True;
+       return NT_STATUS_OK;
 }
 
 NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                        struct rpc_pipe_client **cli, struct policy_handle *sam_handle)
 {
        struct winbindd_cm_conn *conn;
-       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       NTSTATUS status, result;
        struct netlogon_creds_CredentialState *p_creds;
        char *machine_password = NULL;
        char *machine_account = NULL;
        char *domain_name = NULL;
 
-       result = init_dc_connection(domain);
-       if (!NT_STATUS_IS_OK(result)) {
-               return result;
+       if (sid_check_is_domain(&domain->sid)) {
+               return open_internal_samr_conn(mem_ctx, domain, cli, sam_handle);
+       }
+
+       status = init_dc_connection_rpc(domain);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
        conn = &domain->conn;
 
-       if (conn->samr_pipe != NULL) {
+       if (rpccli_is_connected(conn->samr_pipe)) {
                goto done;
        }
 
+       TALLOC_FREE(conn->samr_pipe);
 
        /*
         * No SAMR pipe yet. Attempt to get an NTLMSSP SPNEGO authenticated
@@ -2034,9 +2186,9 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
            (conn->cli->domain[0] == '\0') || 
            (conn->cli->password == NULL || conn->cli->password[0] == '\0'))
        {
-               result = get_trust_creds(domain, &machine_password,
+               status = get_trust_creds(domain, &machine_password,
                                         &machine_account, NULL);
-               if (!NT_STATUS_IS_OK(result)) {
+               if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(10, ("cm_connect_sam: No no user available for "
                                   "domain %s, trying schannel\n", conn->cli->domain));
                        goto schannel;
@@ -2049,27 +2201,29 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        }
 
        if (!machine_password || !machine_account) {
-               result = NT_STATUS_NO_MEMORY;
+               status = NT_STATUS_NO_MEMORY;
                goto done;
        }
 
        /* We have an authenticated connection. Use a NTLMSSP SPNEGO
           authenticated SAMR pipe with sign & seal. */
-       result = cli_rpc_pipe_open_spnego_ntlmssp(conn->cli,
-                                                 &ndr_table_samr.syntax_id,
-                                                 NCACN_NP,
-                                                 DCERPC_AUTH_LEVEL_PRIVACY,
-                                                 domain_name,
-                                                 machine_account,
-                                                 machine_password,
-                                                 &conn->samr_pipe);
+       status = cli_rpc_pipe_open_spnego(conn->cli,
+                                         &ndr_table_samr,
+                                         NCACN_NP,
+                                         GENSEC_OID_NTLMSSP,
+                                         DCERPC_AUTH_LEVEL_PRIVACY,
+                                         smbXcli_conn_remote_name(conn->cli->conn),
+                                         domain_name,
+                                         machine_account,
+                                         machine_password,
+                                         &conn->samr_pipe);
 
-       if (!NT_STATUS_IS_OK(result)) {
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cm_connect_sam: failed to connect to SAMR "
                          "pipe for domain %s using NTLMSSP "
                          "authenticated pipe: user %s\\%s. Error was "
                          "%s\n", domain->name, domain_name,
-                         machine_account, nt_errstr(result)));
+                         machine_account, nt_errstr(status)));
                goto schannel;
        }
 
@@ -2078,86 +2232,111 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                  "pipe: user %s\\%s\n", domain->name,
                  domain_name, machine_account));
 
-       result = rpccli_samr_Connect2(conn->samr_pipe, mem_ctx,
+       status = dcerpc_samr_Connect2(conn->samr_pipe->binding_handle, mem_ctx,
                                      conn->samr_pipe->desthost,
                                      SEC_FLAG_MAXIMUM_ALLOWED,
-                                     &conn->sam_connect_handle);
-       if (NT_STATUS_IS_OK(result)) {
+                                     &conn->sam_connect_handle,
+                                     &result);
+       if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
                goto open_domain;
        }
-       DEBUG(10,("cm_connect_sam: ntlmssp-sealed rpccli_samr_Connect2 "
+       if (NT_STATUS_IS_OK(status)) {
+               status = result;
+       }
+
+       DEBUG(10,("cm_connect_sam: ntlmssp-sealed dcerpc_samr_Connect2 "
                  "failed for domain %s, error was %s. Trying schannel\n",
-                 domain->name, nt_errstr(result) ));
+                 domain->name, nt_errstr(status) ));
        TALLOC_FREE(conn->samr_pipe);
 
  schannel:
 
        /* Fall back to schannel if it's a W2K pre-SP1 box. */
 
-       if (!cm_get_schannel_creds(domain, &p_creds)) {
+       status = cm_get_schannel_creds(domain, &p_creds);
+       if (!NT_STATUS_IS_OK(status)) {
                /* If this call fails - conn->cli can now be NULL ! */
                DEBUG(10, ("cm_connect_sam: Could not get schannel auth info "
-                          "for domain %s, trying anon\n", domain->name));
+                          "for domain %s (error %s), trying anon\n",
+                       domain->name,
+                       nt_errstr(status) ));
                goto anonymous;
        }
-       result = cli_rpc_pipe_open_schannel_with_key
+       status = cli_rpc_pipe_open_schannel_with_key
                (conn->cli, &ndr_table_samr.syntax_id, NCACN_NP,
                 DCERPC_AUTH_LEVEL_PRIVACY,
                 domain->name, &p_creds, &conn->samr_pipe);
 
-       if (!NT_STATUS_IS_OK(result)) {
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cm_connect_sam: failed to connect to SAMR pipe for "
                          "domain %s using schannel. Error was %s\n",
-                         domain->name, nt_errstr(result) ));
+                         domain->name, nt_errstr(status) ));
                goto anonymous;
        }
        DEBUG(10,("cm_connect_sam: connected to SAMR pipe for domain %s using "
                  "schannel.\n", domain->name ));
 
-       result = rpccli_samr_Connect2(conn->samr_pipe, mem_ctx,
+       status = dcerpc_samr_Connect2(conn->samr_pipe->binding_handle, mem_ctx,
                                      conn->samr_pipe->desthost,
                                      SEC_FLAG_MAXIMUM_ALLOWED,
-                                     &conn->sam_connect_handle);
-       if (NT_STATUS_IS_OK(result)) {
+                                     &conn->sam_connect_handle,
+                                     &result);
+       if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
                goto open_domain;
        }
-       DEBUG(10,("cm_connect_sam: schannel-sealed rpccli_samr_Connect2 failed "
+       if (NT_STATUS_IS_OK(status)) {
+               status = result;
+       }
+       DEBUG(10,("cm_connect_sam: schannel-sealed dcerpc_samr_Connect2 failed "
                  "for domain %s, error was %s. Trying anonymous\n",
-                 domain->name, nt_errstr(result) ));
+                 domain->name, nt_errstr(status) ));
        TALLOC_FREE(conn->samr_pipe);
 
  anonymous:
 
        /* Finally fall back to anonymous. */
-       result = cli_rpc_pipe_open_noauth(conn->cli, &ndr_table_samr.syntax_id,
+       status = cli_rpc_pipe_open_noauth(conn->cli, &ndr_table_samr.syntax_id,
                                          &conn->samr_pipe);
 
-       if (!NT_STATUS_IS_OK(result)) {
+       if (!NT_STATUS_IS_OK(status)) {
                goto done;
        }
 
-       result = rpccli_samr_Connect2(conn->samr_pipe, mem_ctx,
+       status = dcerpc_samr_Connect2(conn->samr_pipe->binding_handle, mem_ctx,
                                      conn->samr_pipe->desthost,
                                      SEC_FLAG_MAXIMUM_ALLOWED,
-                                     &conn->sam_connect_handle);
-       if (!NT_STATUS_IS_OK(result)) {
+                                     &conn->sam_connect_handle,
+                                     &result);
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10,("cm_connect_sam: rpccli_samr_Connect2 failed "
                          "for domain %s Error was %s\n",
-                         domain->name, nt_errstr(result) ));
+                         domain->name, nt_errstr(status) ));
+               goto done;
+       }
+       if (!NT_STATUS_IS_OK(result)) {
+               status = result;
+               DEBUG(10,("cm_connect_sam: dcerpc_samr_Connect2 failed "
+                         "for domain %s Error was %s\n",
+                         domain->name, nt_errstr(result)));
                goto done;
        }
 
  open_domain:
-       result = rpccli_samr_OpenDomain(conn->samr_pipe,
+       status = dcerpc_samr_OpenDomain(conn->samr_pipe->binding_handle,
                                        mem_ctx,
                                        &conn->sam_connect_handle,
                                        SEC_FLAG_MAXIMUM_ALLOWED,
                                        &domain->sid,
-                                       &conn->sam_domain_handle);
+                                       &conn->sam_domain_handle,
+                                       &result);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
 
+       status = result;
  done:
 
-       if (NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
+       if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
                /*
                 * if we got access denied, we might just have no access rights
                 * to talk to the remote samr server server (e.g. when we are a
@@ -2167,17 +2346,17 @@ NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
                 */
                TALLOC_FREE(conn->samr_pipe);
                ZERO_STRUCT(conn->sam_domain_handle);
-               return result;
-       } else if (!NT_STATUS_IS_OK(result)) {
+               return status;
+       } else if (!NT_STATUS_IS_OK(status)) {
                invalidate_cm_connection(conn);
-               return result;
+               return status;
        }
 
        *cli = conn->samr_pipe;
        *sam_handle = conn->sam_domain_handle;
        SAFE_FREE(machine_password);
        SAFE_FREE(machine_account);
-       return result;
+       return status;
 }
 
 /**********************************************************************
@@ -2189,11 +2368,12 @@ NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
                            struct rpc_pipe_client **cli)
 {
        struct winbindd_cm_conn *conn;
+       struct netlogon_creds_CredentialState *creds;
        NTSTATUS status;
 
        DEBUG(10,("cm_connect_lsa_tcp\n"));
 
-       status = init_dc_connection(domain);
+       status = init_dc_connection_rpc(domain);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -2203,20 +2383,26 @@ NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
        if (conn->lsa_pipe_tcp &&
            conn->lsa_pipe_tcp->transport->transport == NCACN_IP_TCP &&
            conn->lsa_pipe_tcp->auth->auth_level == DCERPC_AUTH_LEVEL_PRIVACY &&
-           rpc_pipe_tcp_connection_ok(conn->lsa_pipe_tcp)) {
+           rpccli_is_connected(conn->lsa_pipe_tcp)) {
                goto done;
        }
 
        TALLOC_FREE(conn->lsa_pipe_tcp);
 
-       status = cli_rpc_pipe_open_schannel(conn->cli,
-                                           &ndr_table_lsarpc.syntax_id,
-                                           NCACN_IP_TCP,
-                                           DCERPC_AUTH_LEVEL_PRIVACY,
-                                           domain->name,
-                                           &conn->lsa_pipe_tcp);
+       status = cm_get_schannel_creds(domain, &creds);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10,("cli_rpc_pipe_open_schannel failed: %s\n",
+               goto done;
+       }
+
+       status = cli_rpc_pipe_open_schannel_with_key(conn->cli,
+                                                    &ndr_table_lsarpc.syntax_id,
+                                                    NCACN_IP_TCP,
+                                                    DCERPC_AUTH_LEVEL_PRIVACY,
+                                                    domain->name,
+                                                    &creds,
+                                                    &conn->lsa_pipe_tcp);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10,("cli_rpc_pipe_open_schannel_with_key failed: %s\n",
                        nt_errstr(status)));
                goto done;
        }
@@ -2239,16 +2425,18 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
        struct netlogon_creds_CredentialState *p_creds;
 
-       result = init_dc_connection(domain);
+       result = init_dc_connection_rpc(domain);
        if (!NT_STATUS_IS_OK(result))
                return result;
 
        conn = &domain->conn;
 
-       if (conn->lsa_pipe != NULL) {
+       if (rpccli_is_connected(conn->lsa_pipe)) {
                goto done;
        }
 
+       TALLOC_FREE(conn->lsa_pipe);
+
        if ((conn->cli->user_name[0] == '\0') ||
            (conn->cli->domain[0] == '\0') || 
            (conn->cli->password == NULL || conn->cli->password[0] == '\0')) {
@@ -2259,9 +2447,11 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 
        /* We have an authenticated connection. Use a NTLMSSP SPNEGO
         * authenticated LSA pipe with sign & seal. */
-       result = cli_rpc_pipe_open_spnego_ntlmssp
-               (conn->cli, &ndr_table_lsarpc.syntax_id, NCACN_NP,
+       result = cli_rpc_pipe_open_spnego
+               (conn->cli, &ndr_table_lsarpc, NCACN_NP,
+                GENSEC_OID_NTLMSSP,
                 DCERPC_AUTH_LEVEL_PRIVACY,
+                smbXcli_conn_remote_name(conn->cli->conn),
                 conn->cli->domain, conn->cli->user_name, conn->cli->password,
                 &conn->lsa_pipe);
 
@@ -2294,10 +2484,13 @@ NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
 
        /* Fall back to schannel if it's a W2K pre-SP1 box. */
 
-       if (!cm_get_schannel_creds(domain, &p_creds)) {
+       result = cm_get_schannel_creds(domain, &p_creds);
+       if (!NT_STATUS_IS_OK(result)) {
                /* If this call fails - conn->cli can now be NULL ! */
                DEBUG(10, ("cm_connect_lsa: Could not get schannel auth info "
-                          "for domain %s, trying anon\n", domain->name));
+                          "for domain %s (error %s), trying anon\n",
+                       domain->name,
+                       nt_errstr(result) ));
                goto anonymous;
        }
        result = cli_rpc_pipe_open_schannel_with_key
@@ -2369,18 +2562,20 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
 
        *cli = NULL;
 
-       result = init_dc_connection(domain);
+       result = init_dc_connection_rpc(domain);
        if (!NT_STATUS_IS_OK(result)) {
                return result;
        }
 
        conn = &domain->conn;
 
-       if (conn->netlogon_pipe != NULL) {
+       if (rpccli_is_connected(conn->netlogon_pipe)) {
                *cli = conn->netlogon_pipe;
                return NT_STATUS_OK;
        }
 
+       TALLOC_FREE(conn->netlogon_pipe);
+
        result = cli_rpc_pipe_open_noauth(conn->cli,
                                          &ndr_table_netlogon.syntax_id,
                                          &netlogon_pipe);
@@ -2409,7 +2604,7 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
                 netlogon_pipe,
                 domain->dcname, /* server name. */
                 domain->name,   /* domain name */
-                global_myname(), /* client name */
+                lp_netbios_name(), /* client name */
                 account_name,   /* machine account */
                 mach_pwd,       /* machine password */
                 sec_chan_type,  /* from get_trust_pw */
@@ -2459,8 +2654,8 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
                DEBUG(3, ("Could not open schannel'ed NETLOGON pipe. Error "
                          "was %s\n", nt_errstr(result)));
 
-               /* make sure we return something besides OK */
-               return !NT_STATUS_IS_OK(result) ? result : NT_STATUS_PIPE_NOT_AVAILABLE;
+               invalidate_cm_connection(conn);
+               return result;
        }
 
        /*
@@ -2475,3 +2670,63 @@ NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
        *cli = conn->netlogon_pipe;
        return NT_STATUS_OK;
 }
+
+void winbind_msg_ip_dropped(struct messaging_context *msg_ctx,
+                           void *private_data,
+                           uint32_t msg_type,
+                           struct server_id server_id,
+                           DATA_BLOB *data)
+{
+       struct winbindd_domain *domain;
+       char *freeit = NULL;
+       char *addr;
+
+       if ((data == NULL)
+           || (data->data == NULL)
+           || (data->length == 0)
+           || (data->data[data->length-1] != '\0')) {
+               DEBUG(1, ("invalid msg_ip_dropped message: not a valid "
+                         "string\n"));
+               return;
+       }
+
+       addr = (char *)data->data;
+       DEBUG(10, ("IP %s dropped\n", addr));
+
+       if (!is_ipaddress(addr)) {
+               char *slash;
+               /*
+                * Some code sends us ip addresses with the /netmask
+                * suffix
+                */
+               slash = strchr(addr, '/');
+               if (slash == NULL) {
+                       DEBUG(1, ("invalid msg_ip_dropped message: %s",
+                                 addr));
+                       return;
+               }
+               freeit = talloc_strndup(talloc_tos(), addr, slash-addr);
+               if (freeit == NULL) {
+                       DEBUG(1, ("talloc failed\n"));
+                       return;
+               }
+               addr = freeit;
+               DEBUG(10, ("Stripped /netmask to IP %s\n", addr));
+       }
+
+       for (domain = domain_list(); domain != NULL; domain = domain->next) {
+               char sockaddr[INET6_ADDRSTRLEN];
+
+               if (!cli_state_is_connected(domain->conn.cli)) {
+                       continue;
+               }
+
+               print_sockaddr(sockaddr, sizeof(sockaddr),
+                              smbXcli_conn_local_sockaddr(domain->conn.cli->conn));
+
+               if (strequal(sockaddr, addr)) {
+                       cli_state_disconnect(domain->conn.cli);
+               }
+       }
+       TALLOC_FREE(freeit);
+}