Don't leak memory in error path.
[ira/wip.git] / source3 / libsmb / cliconnect.c
index 6b5de6d1439b6d29f1341cd274f43926c28c9436..f3926b777b92e9381e416200fe7fbb80427eb3f1 100644 (file)
@@ -6,7 +6,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
-extern pstring user_socket_options;
-
 static const struct {
        int prot;
        const char *name;
@@ -33,12 +30,15 @@ static const struct {
        {PROTOCOL_LANMAN1,"LANMAN1.0"},
        {PROTOCOL_LANMAN2,"LM1.2X002"},
        {PROTOCOL_LANMAN2,"DOS LANMAN2.1"},
+       {PROTOCOL_LANMAN2,"LANMAN2.1"},
        {PROTOCOL_LANMAN2,"Samba"},
        {PROTOCOL_NT1,"NT LANMAN 1.0"},
        {PROTOCOL_NT1,"NT LM 0.12"},
        {-1,NULL}
 };
 
+static const char *star_smbserver_name = "*SMBSERVER";
+
 /**
  * Set the user session key for a connection
  * @param cli The cli structure to add it too
@@ -55,16 +55,19 @@ static void cli_set_session_key (struct cli_state *cli, const DATA_BLOB session_
  Do an old lanman2 style session setup.
 ****************************************************************************/
 
-static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user, 
-                                     const char *pass, size_t passlen, const char *workgroup)
+static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli,
+                                         const char *user, 
+                                         const char *pass, size_t passlen,
+                                         const char *workgroup)
 {
-       DATA_BLOB session_key = data_blob(NULL, 0);
-       DATA_BLOB lm_response = data_blob(NULL, 0);
+       DATA_BLOB session_key = data_blob_null;
+       DATA_BLOB lm_response = data_blob_null;
        fstring pword;
        char *p;
 
-       if (passlen > sizeof(pword)-1)
-               return False;
+       if (passlen > sizeof(pword)-1) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
 
        /* LANMAN servers predate NT status codes and Unicode and ignore those 
           smb flags so we must disable the corresponding default capabilities  
@@ -82,7 +85,7 @@ static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user,
                lm_response = data_blob(NULL, 24);
                if (!SMBencrypt(pass, cli->secblob.data,(uchar *)lm_response.data)) {
                        DEBUG(1, ("Password is > 14 chars in length, and is therefore incompatible with Lanman authentication\n"));
-                       return False;
+                       return NT_STATUS_ACCESS_DENIED;
                }
        } else if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen == 24) {
                /* Encrypted mode needed, and encrypted password supplied. */
@@ -95,7 +98,7 @@ static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user,
 
        /* send a session setup command */
        memset(cli->outbuf,'\0',smb_size);
-       set_message(cli->outbuf,10, 0, True);
+       cli_set_message(cli->outbuf,10, 0, True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
        
@@ -115,14 +118,15 @@ static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user,
        p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
        cli_setup_bcc(cli, p);
 
-       cli_send_smb(cli);
-       if (!cli_receive_smb(cli))
-               return False;
+       if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+               return cli_nt_error(cli);
+       }
 
        show_msg(cli->inbuf);
 
-       if (cli_is_error(cli))
-               return False;
+       if (cli_is_error(cli)) {
+               return cli_nt_error(cli);
+       }
        
        /* use the returned vuid from now on */
        cli->vuid = SVAL(cli->inbuf,smb_uid);   
@@ -133,7 +137,7 @@ static BOOL cli_session_setup_lanman2(struct cli_state *cli, const char *user,
                cli_set_session_key(cli, session_key);
        }
 
-       return True;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -158,13 +162,13 @@ static uint32 cli_session_setup_capabilities(struct cli_state *cli)
  Do a NT1 guest session setup.
 ****************************************************************************/
 
-static BOOL cli_session_setup_guest(struct cli_state *cli)
+static NTSTATUS cli_session_setup_guest(struct cli_state *cli)
 {
        char *p;
        uint32 capabilities = cli_session_setup_capabilities(cli);
 
        memset(cli->outbuf, '\0', smb_size);
-       set_message(cli->outbuf,13,0,True);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -183,14 +187,15 @@ static BOOL cli_session_setup_guest(struct cli_state *cli)
        p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
        cli_setup_bcc(cli, p);
 
-       cli_send_smb(cli);
-       if (!cli_receive_smb(cli))
-             return False;
+       if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+               return cli_nt_error(cli);
+       }
        
        show_msg(cli->inbuf);
        
-       if (cli_is_error(cli))
-               return False;
+       if (cli_is_error(cli)) {
+               return cli_nt_error(cli);
+       }
 
        cli->vuid = SVAL(cli->inbuf,smb_uid);
 
@@ -205,15 +210,16 @@ static BOOL cli_session_setup_guest(struct cli_state *cli)
 
        fstrcpy(cli->user_name, "");
 
-       return True;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
  Do a NT1 plaintext session setup.
 ****************************************************************************/
 
-static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user, 
-                                       const char *pass, const char *workgroup)
+static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
+                                           const char *user, const char *pass,
+                                           const char *workgroup)
 {
        uint32 capabilities = cli_session_setup_capabilities(cli);
        char *p;
@@ -221,7 +227,8 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user,
        
        fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
 
-       set_message(cli->outbuf,13,0,True);
+       memset(cli->outbuf, '\0', smb_size);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -251,14 +258,15 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user,
        p += clistr_push(cli, p, lanman, -1, STR_TERMINATE);
        cli_setup_bcc(cli, p);
 
-       cli_send_smb(cli);
-       if (!cli_receive_smb(cli))
-             return False;
+       if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+               return cli_nt_error(cli);
+       }
        
        show_msg(cli->inbuf);
        
-       if (cli_is_error(cli))
-               return False;
+       if (cli_is_error(cli)) {
+               return cli_nt_error(cli);
+       }
 
        cli->vuid = SVAL(cli->inbuf,smb_uid);
        p = smb_buf(cli->inbuf);
@@ -271,7 +279,7 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user,
                cli->is_samba = True;
        }
 
-       return True;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
@@ -284,16 +292,16 @@ static BOOL cli_session_setup_plaintext(struct cli_state *cli, const char *user,
    @param workgroup The user's domain.
 ****************************************************************************/
 
-static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user, 
-                                 const char *pass, size_t passlen,
-                                 const char *ntpass, size_t ntpasslen,
-                                 const char *workgroup)
+static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, 
+                                     const char *pass, size_t passlen,
+                                     const char *ntpass, size_t ntpasslen,
+                                     const char *workgroup)
 {
        uint32 capabilities = cli_session_setup_capabilities(cli);
-       DATA_BLOB lm_response = data_blob(NULL, 0);
-       DATA_BLOB nt_response = data_blob(NULL, 0);
-       DATA_BLOB session_key = data_blob(NULL, 0);
-       BOOL ret = False;
+       DATA_BLOB lm_response = data_blob_null;
+       DATA_BLOB nt_response = data_blob_null;
+       DATA_BLOB session_key = data_blob_null;
+       NTSTATUS result;
        char *p;
 
        if (passlen == 0) {
@@ -315,7 +323,7 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
                                              &lm_response, &nt_response, &session_key)) {
                                data_blob_free(&names_blob);
                                data_blob_free(&server_chal);
-                               return False;
+                               return NT_STATUS_ACCESS_DENIED;
                        }
                        data_blob_free(&names_blob);
                        data_blob_free(&server_chal);
@@ -325,7 +333,7 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
                        E_md4hash(pass, nt_hash);
 
 #ifdef LANMAN_ONLY
-                       nt_response = data_blob(NULL, 0);
+                       nt_response = data_blob_null;
 #else
                        nt_response = data_blob(NULL, 24);
                        SMBNTencrypt(pass,cli->secblob.data,nt_response.data);
@@ -369,7 +377,7 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
        /* send a session setup command */
        memset(cli->outbuf,'\0',smb_size);
 
-       set_message(cli->outbuf,13,0,True);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -397,14 +405,14 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
        cli_setup_bcc(cli, p);
 
        if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
-               ret = False;
+               result = cli_nt_error(cli);
                goto end;
        }
 
        /* show_msg(cli->inbuf); */
 
        if (cli_is_error(cli)) {
-               ret = False;
+               result = cli_nt_error(cli);
                goto end;
        }
 
@@ -427,19 +435,19 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, const char *user,
                cli_set_session_key(cli, session_key);
        }
 
-       ret = True;
+       result = NT_STATUS_OK;
 end:   
        data_blob_free(&lm_response);
        data_blob_free(&nt_response);
        data_blob_free(&session_key);
-       return ret;
+       return result;
 }
 
 /****************************************************************************
  Send a extended security session setup blob
 ****************************************************************************/
 
-static BOOL cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob)
+static bool cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob)
 {
        uint32 capabilities = cli_session_setup_capabilities(cli);
        char *p;
@@ -449,7 +457,7 @@ static BOOL cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob)
        /* send a session setup command */
        memset(cli->outbuf,'\0',smb_size);
 
-       set_message(cli->outbuf,12,0,True);
+       cli_set_message(cli->outbuf,12,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
 
        cli_setup_packet(cli);
@@ -476,7 +484,7 @@ static BOOL cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob)
 
 static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
 {
-       DATA_BLOB blob2 = data_blob(NULL, 0);
+       DATA_BLOB blob2 = data_blob_null;
        char *p;
        size_t len;
 
@@ -508,19 +516,79 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
 }
 
 #ifdef HAVE_KRB5
-
 /****************************************************************************
  Send a extended security session setup blob, returning a reply blob.
 ****************************************************************************/
 
-static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
+/* The following is calculated from :
+ * (smb_size-4) = 35
+ * (smb_wcnt * 2) = 24 (smb_wcnt == 12 in cli_session_setup_blob_send() )
+ * (strlen("Unix") + 1 + strlen("Samba") + 1) * 2 = 22 (unicode strings at
+ * end of packet.
+ */
+
+#define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22)
+
+static bool cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob, DATA_BLOB session_key_krb5)
 {
-       DATA_BLOB blob2 = data_blob(NULL, 0);
-       if (!cli_session_setup_blob_send(cli, blob)) {
-               return blob2;
+       int32 remaining = blob.length;
+       int32 cur = 0;
+       DATA_BLOB send_blob = data_blob_null;
+       int32 max_blob_size = 0;
+       DATA_BLOB receive_blob = data_blob_null;
+
+       if (cli->max_xmit < BASE_SESSSETUP_BLOB_PACKET_SIZE + 1) {
+               DEBUG(0,("cli_session_setup_blob: cli->max_xmit too small "
+                       "(was %u, need minimum %u)\n",
+                       (unsigned int)cli->max_xmit,
+                       BASE_SESSSETUP_BLOB_PACKET_SIZE));
+               cli_set_nt_error(cli, NT_STATUS_INVALID_PARAMETER);
+               return False;
        }
-               
-       return cli_session_setup_blob_receive(cli);
+
+       max_blob_size = cli->max_xmit - BASE_SESSSETUP_BLOB_PACKET_SIZE;
+
+       while ( remaining > 0) {
+               if (remaining >= max_blob_size) {
+                       send_blob.length = max_blob_size;
+                       remaining -= max_blob_size;
+               } else {
+                       DATA_BLOB null_blob = data_blob_null;
+
+                       send_blob.length = remaining; 
+                        remaining = 0;
+
+                       /* This is the last packet in the sequence - turn signing on. */
+                       cli_simple_set_signing(cli, session_key_krb5, null_blob); 
+               }
+
+               send_blob.data =  &blob.data[cur];
+               cur += send_blob.length;
+
+               DEBUG(10, ("cli_session_setup_blob: Remaining (%u) sending (%u) current (%u)\n", 
+                       (unsigned int)remaining,
+                       (unsigned int)send_blob.length,
+                       (unsigned int)cur ));
+
+               if (!cli_session_setup_blob_send(cli, send_blob)) {
+                       DEBUG(0, ("cli_session_setup_blob: send failed\n"));
+                       return False;
+               }
+
+               receive_blob = cli_session_setup_blob_receive(cli);
+               data_blob_free(&receive_blob);
+
+               if (cli_is_error(cli) &&
+                               !NT_STATUS_EQUAL( cli_get_nt_error(cli), 
+                                       NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+                       DEBUG(0, ("cli_session_setup_blob: recieve failed (%s)\n",
+                               nt_errstr(cli_get_nt_error(cli)) ));
+                       cli->vuid = 0;
+                       return False;
+               }
+       }
+
+       return True;
 }
 
 /****************************************************************************
@@ -537,18 +605,18 @@ static void use_in_memory_ccache(void) {
 
 static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *principal, const char *workgroup)
 {
-       DATA_BLOB blob2, negTokenTarg;
+       DATA_BLOB negTokenTarg;
        DATA_BLOB session_key_krb5;
-       DATA_BLOB null_blob = data_blob(NULL, 0);
        int rc;
 
        DEBUG(2,("Doing kerberos session setup\n"));
 
        /* generate the encapsulated kerberos5 ticket */
-       rc = spnego_gen_negTokenTarg(principal, 0, &negTokenTarg, &session_key_krb5, 0);
+       rc = spnego_gen_negTokenTarg(principal, 0, &negTokenTarg, &session_key_krb5, 0, NULL);
 
        if (rc) {
-               DEBUG(1, ("spnego_gen_negTokenTarg failed: %s\n", error_message(rc)));
+               DEBUG(1, ("cli_session_setup_kerberos: spnego_gen_negTokenTarg failed: %s\n",
+                       error_message(rc)));
                return ADS_ERROR_KRB5(rc);
        }
 
@@ -556,12 +624,11 @@ static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *
        file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length);
 #endif
 
-       cli_simple_set_signing(cli, session_key_krb5, null_blob); 
-                       
-       blob2 = cli_session_setup_blob(cli, negTokenTarg);
-
-       /* we don't need this blob for kerberos */
-       data_blob_free(&blob2);
+       if (!cli_session_setup_blob(cli, negTokenTarg, session_key_krb5)) {
+               data_blob_free(&negTokenTarg);
+               data_blob_free(&session_key_krb5);
+               ADS_ERROR_NT(cli_nt_error(cli));
+       }
 
        cli_set_session_key(cli, session_key_krb5);
 
@@ -589,15 +656,16 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
        NTSTATUS nt_status;
        int turn = 1;
        DATA_BLOB msg1;
-       DATA_BLOB blob = data_blob(NULL, 0);
-       DATA_BLOB blob_in = data_blob(NULL, 0);
-       DATA_BLOB blob_out = data_blob(NULL, 0);
+       DATA_BLOB blob = data_blob_null;
+       DATA_BLOB blob_in = data_blob_null;
+       DATA_BLOB blob_out = data_blob_null;
 
        cli_temp_set_signing(cli);
 
        if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
                return nt_status;
        }
+       ntlmssp_want_feature(ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
 
        if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) {
                return nt_status;
@@ -627,19 +695,18 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                                DEBUG(3, ("Failed to send NTLMSSP/SPNEGO blob to server!\n"));
                                nt_status = NT_STATUS_UNSUCCESSFUL;
                        } else {
-                               data_blob_free(&msg1);
-                               
                                blob = cli_session_setup_blob_receive(cli);
                                
                                nt_status = cli_nt_error(cli);
                                if (cli_is_error(cli) && NT_STATUS_IS_OK(nt_status)) {
-                                       if (cli->smb_rw_error == READ_BAD_SIG) {
+                                       if (cli->smb_rw_error == SMB_READ_BAD_SIG) {
                                                nt_status = NT_STATUS_ACCESS_DENIED;
                                        } else {
                                                nt_status = NT_STATUS_UNSUCCESSFUL;
                                        }
                                }
                        }
+                       data_blob_free(&msg1);
                }
                
                if (!blob.length) {
@@ -648,7 +715,7 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                        }
                } else if ((turn == 1) && 
                           NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-                       DATA_BLOB tmp_blob = data_blob(NULL, 0);
+                       DATA_BLOB tmp_blob = data_blob_null;
                        /* the server might give us back two challenges */
                        if (!spnego_parse_challenge(blob, &blob_in, 
                                                    &tmp_blob)) {
@@ -657,7 +724,7 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                        }
                        data_blob_free(&tmp_blob);
                } else {
-                       if (!spnego_parse_auth_response(blob, nt_status, 
+                       if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP, 
                                                        &blob_in)) {
                                DEBUG(3,("Failed to parse auth response\n"));
                                if (NT_STATUS_IS_OK(nt_status) 
@@ -670,12 +737,14 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                turn++;
        } while (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED));
 
+       data_blob_free(&blob_in);
+
        if (NT_STATUS_IS_OK(nt_status)) {
 
                DATA_BLOB key = data_blob(ntlmssp_state->session_key.data,
                                          ntlmssp_state->session_key.length);
-               DATA_BLOB null_blob = data_blob(NULL, 0);
-               BOOL res;
+               DATA_BLOB null_blob = data_blob_null;
+               bool res;
 
                fstrcpy(cli->server_domain, ntlmssp_state->server_domain);
                cli_set_session_key(cli, ntlmssp_state->session_key);
@@ -701,6 +770,9 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
 
        ntlmssp_end(&ntlmssp_state);
 
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               cli->vuid = 0;
+       }
        return nt_status;
 }
 
@@ -714,9 +786,7 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
        char *principal;
        char *OIDs[ASN1_MAX_OIDS];
        int i;
-#ifdef HAVE_KRB5
-       BOOL got_kerberos_mechanism = False;
-#endif
+       bool got_kerberos_mechanism = False;
        DATA_BLOB blob;
 
        DEBUG(3,("Doing spnego session setup (blob length=%lu)\n", (unsigned long)cli->secblob.length));
@@ -745,15 +815,26 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
        /* make sure the server understands kerberos */
        for (i=0;OIDs[i];i++) {
                DEBUG(3,("got OID=%s\n", OIDs[i]));
-#ifdef HAVE_KRB5
                if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
                    strcmp(OIDs[i], OID_KERBEROS5) == 0) {
                        got_kerberos_mechanism = True;
                }
-#endif
                free(OIDs[i]);
        }
-       DEBUG(3,("got principal=%s\n", principal));
+
+       DEBUG(3,("got principal=%s\n", principal ? principal : "<null>"));
+
+       if (got_kerberos_mechanism && (principal == NULL)) {
+               /*
+                * It is WRONG to depend on the principal sent in the negprot
+                * reply, but right now we do it. So for safety (don't
+                * segfault later) disable Kerberos when no principal was
+                * sent. -- VL
+                */
+               DEBUG(1, ("Kerberos mech was offered, but no principal was "
+                         "sent, disabling Kerberos\n"));
+               cli->use_kerberos = False;
+       }
 
        fstrcpy(cli->user_name, user);
 
@@ -779,10 +860,56 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
                        }
                }
                
-               rc = cli_session_setup_kerberos(cli, principal, domain);
-               if (ADS_ERR_OK(rc) || !cli->fallback_after_kerberos) {
+               /* If we get a bad principal, try to guess it if
+                  we have a valid host NetBIOS name.
+                */
+               if (strequal(principal, ADS_IGNORE_PRINCIPAL)) {
                        SAFE_FREE(principal);
-                       return rc;
+               }
+
+               if (principal == NULL &&
+                       !is_ipaddress(cli->desthost) &&
+                       !strequal(star_smbserver_name,
+                               cli->desthost)) {
+                       char *realm = NULL;
+                       char *machine = NULL;
+                       char *host = NULL;
+                       DEBUG(3,("cli_session_setup_spnego: got a "
+                               "bad server principal, trying to guess ...\n"));
+
+                       host = strchr_m(cli->desthost, '.');
+                       if (host) {
+                               machine = SMB_STRNDUP(cli->desthost,
+                                       host - cli->desthost);
+                       } else {
+                               machine = SMB_STRDUP(cli->desthost);
+                       }
+                       if (machine == NULL) {
+                               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       }
+
+                       realm = kerberos_get_default_realm_from_ccache();
+                       if (realm && *realm) {
+                               if (asprintf(&principal, "%s$@%s",
+                                               machine, realm) < 0) {
+                                       SAFE_FREE(machine);
+                                       SAFE_FREE(realm);
+                                       return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                               }
+                               DEBUG(3,("cli_session_setup_spnego: guessed "
+                                       "server principal=%s\n",
+                                       principal ? principal : "<null>"));
+                       }
+                       SAFE_FREE(machine);
+                       SAFE_FREE(realm);
+               }
+
+               if (principal) {
+                       rc = cli_session_setup_kerberos(cli, principal, domain);
+                       if (ADS_ERR_OK(rc) || !cli->fallback_after_kerberos) {
+                               SAFE_FREE(principal);
+                               return rc;
+                       }
                }
        }
 #endif
@@ -800,17 +927,26 @@ ntlmssp:
  password is in plaintext, the same should be done.
 ****************************************************************************/
 
-BOOL cli_session_setup(struct cli_state *cli, 
-                      const char *user, 
-                      const char *pass, int passlen,
-                      const char *ntpass, int ntpasslen,
-                      const char *workgroup)
+NTSTATUS cli_session_setup(struct cli_state *cli,
+                          const char *user,
+                          const char *pass, int passlen,
+                          const char *ntpass, int ntpasslen,
+                          const char *workgroup)
 {
        char *p;
        fstring user2;
 
+       if (user) {
+               fstrcpy(user2, user);
+       } else {
+               user2[0] ='\0';
+       }
+
+       if (!workgroup) {
+               workgroup = "";
+       }
+
        /* allow for workgroups as part of the username */
-       fstrcpy(user2, user);
        if ((p=strchr_m(user2,'\\')) || (p=strchr_m(user2,'/')) ||
            (p=strchr_m(user2,*lp_winbind_separator()))) {
                *p = 0;
@@ -818,8 +954,9 @@ BOOL cli_session_setup(struct cli_state *cli,
                workgroup = user2;
        }
 
-       if (cli->protocol < PROTOCOL_LANMAN1)
-               return True;
+       if (cli->protocol < PROTOCOL_LANMAN1) {
+               return NT_STATUS_OK;
+       }
 
        /* now work out what sort of session setup we are going to
            do. I have split this into separate functions to make the
@@ -831,17 +968,18 @@ BOOL cli_session_setup(struct cli_state *cli,
                if (!lp_client_lanman_auth() && passlen != 24 && (*pass)) {
                        DEBUG(1, ("Server requested LM password but 'client lanman auth'"
                                  " is disabled\n"));
-                       return False;
+                       return NT_STATUS_ACCESS_DENIED;
                }
 
                if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0 &&
                    !lp_client_plaintext_auth() && (*pass)) {
-                       DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
-                                 " is disabled\n"));
-                       return False;
+                       DEBUG(1, ("Server requested plaintext password but "
+                                 "'client plaintext auth' is disabled\n"));
+                       return NT_STATUS_ACCESS_DENIED;
                }
 
-               return cli_session_setup_lanman2(cli, user, pass, passlen, workgroup);
+               return cli_session_setup_lanman2(cli, user, pass, passlen,
+                                                workgroup);
        }
 
        /* if no user is supplied then we have to do an anonymous connection.
@@ -862,9 +1000,9 @@ BOOL cli_session_setup(struct cli_state *cli,
 
        if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
                if (!lp_client_plaintext_auth() && (*pass)) {
-                       DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
-                                 " is disabled\n"));
-                       return False;
+                       DEBUG(1, ("Server requested plaintext password but "
+                                 "'client plaintext auth' is disabled\n"));
+                       return NT_STATUS_ACCESS_DENIED;
                }
                return cli_session_setup_plaintext(cli, user, pass, workgroup);
        }
@@ -875,13 +1013,18 @@ BOOL cli_session_setup(struct cli_state *cli,
                ADS_STATUS status = cli_session_setup_spnego(cli, user, pass, workgroup);
                if (!ADS_ERR_OK(status)) {
                        DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
-                       return False;
+                       return ads_ntstatus(status);
                }
        } else {
+               NTSTATUS status;
+
                /* otherwise do a NT1 style session setup */
-               if ( !cli_session_setup_nt1(cli, user, pass, passlen, ntpass, ntpasslen, workgroup) ) {
-                       DEBUG(3,("cli_session_setup: NT1 session setup failed!\n"));
-                       return False;
+               status = cli_session_setup_nt1(cli, user, pass, passlen,
+                                              ntpass, ntpasslen, workgroup);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(3,("cli_session_setup: NT1 session setup "
+                                "failed: %s\n", nt_errstr(status)));
+                       return status;
                }
        }
 
@@ -889,7 +1032,7 @@ BOOL cli_session_setup(struct cli_state *cli,
                cli->is_samba = True;
        }
 
-       return True;
+       return NT_STATUS_OK;
 
 }
 
@@ -897,10 +1040,10 @@ BOOL cli_session_setup(struct cli_state *cli,
  Send a uloggoff.
 *****************************************************************************/
 
-BOOL cli_ulogoff(struct cli_state *cli)
+bool cli_ulogoff(struct cli_state *cli)
 {
        memset(cli->outbuf,'\0',smb_size);
-       set_message(cli->outbuf,2,0,True);
+       cli_set_message(cli->outbuf,2,0,True);
        SCVAL(cli->outbuf,smb_com,SMBulogoffX);
        cli_setup_packet(cli);
        SSVAL(cli->outbuf,smb_vwv0,0xFF);
@@ -921,7 +1064,8 @@ BOOL cli_ulogoff(struct cli_state *cli)
 /****************************************************************************
  Send a tconX.
 ****************************************************************************/
-BOOL cli_send_tconX(struct cli_state *cli, 
+
+bool cli_send_tconX(struct cli_state *cli, 
                    const char *share, const char *dev, const char *pass, int passlen)
 {
        fstring fullshare, pword;
@@ -935,9 +1079,13 @@ BOOL cli_send_tconX(struct cli_state *cli,
        if (cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
                passlen = 1;
                pass = "";
+       } else if (!pass) {
+               DEBUG(1, ("Server not using user level security and no password supplied.\n"));
+               return False;
        }
 
-       if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && *pass && passlen != 24) {
+       if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) &&
+           *pass && passlen != 24) {
                if (!lp_client_lanman_auth()) {
                        DEBUG(1, ("Server requested LANMAN password (share-level security) but 'client use lanman auth'"
                                  " is disabled\n"));
@@ -952,8 +1100,9 @@ BOOL cli_send_tconX(struct cli_state *cli,
        } else {
                if((cli->sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL|NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)) == 0) {
                        if (!lp_client_plaintext_auth() && (*pass)) {
-                               DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
-                                         " is disabled\n"));
+                               DEBUG(1, ("Server requested plaintext "
+                                         "password but 'client plaintext "
+                                         "auth' is disabled\n"));
                                return False;
                        }
 
@@ -963,22 +1112,27 @@ BOOL cli_send_tconX(struct cli_state *cli,
                        passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
                        
                } else {
-                       memcpy(pword, pass, passlen);
+                       if (passlen) {
+                               memcpy(pword, pass, passlen);
+                       }
                }
        }
 
        slprintf(fullshare, sizeof(fullshare)-1,
                 "\\\\%s\\%s", cli->desthost, share);
 
-       set_message(cli->outbuf,4, 0, True);
+       cli_set_message(cli->outbuf,4, 0, True);
        SCVAL(cli->outbuf,smb_com,SMBtconX);
        cli_setup_packet(cli);
 
        SSVAL(cli->outbuf,smb_vwv0,0xFF);
+       SSVAL(cli->outbuf,smb_vwv2,TCONX_FLAG_EXTENDED_RESPONSE);
        SSVAL(cli->outbuf,smb_vwv3,passlen);
 
        p = smb_buf(cli->outbuf);
-       memcpy(p,pword,passlen);
+       if (passlen) {
+               memcpy(p,pword,passlen);
+       }
        p += passlen;
        p += clistr_push(cli, p, fullshare, -1, STR_TERMINATE |STR_UPPER);
        p += clistr_push(cli, p, dev, -1, STR_TERMINATE |STR_UPPER | STR_ASCII);
@@ -1015,10 +1169,10 @@ BOOL cli_send_tconX(struct cli_state *cli,
  Send a tree disconnect.
 ****************************************************************************/
 
-BOOL cli_tdis(struct cli_state *cli)
+bool cli_tdis(struct cli_state *cli)
 {
        memset(cli->outbuf,'\0',smb_size);
-       set_message(cli->outbuf,0,0,True);
+       cli_set_message(cli->outbuf,0,0,True);
        SCVAL(cli->outbuf,smb_com,SMBtdis);
        SSVAL(cli->outbuf,smb_tid,cli->cnum);
        cli_setup_packet(cli);
@@ -1050,7 +1204,7 @@ void cli_negprot_send(struct cli_state *cli)
        memset(cli->outbuf,'\0',smb_size);
 
        /* setup the protocol strings */
-       set_message(cli->outbuf,0,0,True);
+       cli_set_message(cli->outbuf,0,0,True);
 
        p = smb_buf(cli->outbuf);
        for (numprots=0;
@@ -1073,7 +1227,7 @@ void cli_negprot_send(struct cli_state *cli)
  Send a negprot command.
 ****************************************************************************/
 
-BOOL cli_negprot(struct cli_state *cli)
+bool cli_negprot(struct cli_state *cli)
 {
        char *p;
        int numprots;
@@ -1090,7 +1244,7 @@ BOOL cli_negprot(struct cli_state *cli)
             numprots++)
                plength += strlen(prots[numprots].name)+2;
     
-       set_message(cli->outbuf,0,plength,True);
+       cli_set_message(cli->outbuf,0,plength,True);
 
        p = smb_buf(cli->outbuf);
        for (numprots=0;
@@ -1124,6 +1278,7 @@ BOOL cli_negprot(struct cli_state *cli)
        }
 
        if (cli->protocol >= PROTOCOL_NT1) {    
+               struct timespec ts;
                /* NT protocol */
                cli->sec_mode = CVAL(cli->inbuf,smb_vwv1);
                cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1);
@@ -1132,7 +1287,8 @@ BOOL cli_negprot(struct cli_state *cli)
                cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1);
                cli->serverzone *= 60;
                /* this time arrives in real GMT */
-               cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
+               ts = interpret_long_date(cli->inbuf+smb_vwv11+1);
+               cli->servertime = ts.tv_sec;
                cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
                cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
                if (cli->capabilities & CAP_RAW_MODE) {
@@ -1213,7 +1369,7 @@ BOOL cli_negprot(struct cli_state *cli)
  Send a session request. See rfc1002.txt 4.3 and 4.3.2.
 ****************************************************************************/
 
-BOOL cli_session_request(struct cli_state *cli,
+bool cli_session_request(struct cli_state *cli,
                         struct nmb_name *calling, struct nmb_name *called)
 {
        char *p;
@@ -1266,22 +1422,28 @@ BOOL cli_session_request(struct cli_state *cli,
                int16 port;
                };
                */
-               int port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9);
+               uint16_t port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9);
+               struct in_addr dest_ip;
+
                /* SESSION RETARGET */
-               putip((char *)&cli->dest_ip,cli->inbuf+4);
+               putip((char *)&dest_ip,cli->inbuf+4);
+               in_addr_to_sockaddr_storage(&cli->dest_ss, dest_ip);
 
-               cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, LONG_CONNECT_TIMEOUT);
+               cli->fd = open_socket_out(SOCK_STREAM,
+                               &cli->dest_ss,
+                               port,
+                               LONG_CONNECT_TIMEOUT);
                if (cli->fd == -1)
                        return False;
 
                DEBUG(3,("Retargeted\n"));
 
-               set_socket_options(cli->fd,user_socket_options);
+               set_socket_options(cli->fd, lp_socket_options());
 
                /* Try again */
                {
                        static int depth;
-                       BOOL ret;
+                       bool ret;
                        if (depth > 4) {
                                DEBUG(0,("Retarget recursion - failing\n"));
                                return False;
@@ -1305,78 +1467,116 @@ BOOL cli_session_request(struct cli_state *cli,
  Open the client sockets.
 ****************************************************************************/
 
-BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
+NTSTATUS cli_connect(struct cli_state *cli,
+               const char *host,
+               struct sockaddr_storage *dest_ss)
+
 {
        int name_type = 0x20;
-       char *p;
+       TALLOC_CTX *frame = talloc_stackframe();
+       unsigned int num_addrs = 0;
+       unsigned int i = 0;
+       struct sockaddr_storage *ss_arr = NULL;
+       char *p = NULL;
 
        /* reasonable default hostname */
-       if (!host) host = "*SMBSERVER";
+       if (!host) {
+               host = star_smbserver_name;
+       }
 
        fstrcpy(cli->desthost, host);
 
        /* allow hostnames of the form NAME#xx and do a netbios lookup */
        if ((p = strchr(cli->desthost, '#'))) {
-               name_type = strtol(p+1, NULL, 16);              
+               name_type = strtol(p+1, NULL, 16);
                *p = 0;
        }
-       
-       if (!ip || is_zero_ip(*ip)) {
-                if (!resolve_name(cli->desthost, &cli->dest_ip, name_type)) {
-                        return False;
+
+       if (!dest_ss || is_zero_addr(dest_ss)) {
+               NTSTATUS status =resolve_name_list(frame,
+                                       cli->desthost,
+                                       name_type,
+                                       &ss_arr,
+                                       &num_addrs);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_BAD_NETWORK_NAME;
                 }
-               if (ip) *ip = cli->dest_ip;
        } else {
-               cli->dest_ip = *ip;
+               num_addrs = 1;
+               ss_arr = TALLOC_P(frame, struct sockaddr_storage);
+               if (!ss_arr) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               *ss_arr = *dest_ss;
        }
 
-       if (getenv("LIBSMB_PROG")) {
-               cli->fd = sock_exec(getenv("LIBSMB_PROG"));
-       } else {
-               /* try 445 first, then 139 */
-               int port = cli->port?cli->port:445;
-               cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, 
-                                         port, cli->timeout);
-               if (cli->fd == -1 && cli->port == 0) {
-                       port = 139;
-                       cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, 
+       for (i = 0; i < num_addrs; i++) {
+               cli->dest_ss = ss_arr[i];
+               if (getenv("LIBSMB_PROG")) {
+                       cli->fd = sock_exec(getenv("LIBSMB_PROG"));
+               } else {
+                       /* try 445 first, then 139 */
+                       uint16_t port = cli->port?cli->port:445;
+                       cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ss,
                                                  port, cli->timeout);
+                       if (cli->fd == -1 && cli->port == 0) {
+                               port = 139;
+                               cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ss,
+                                                         port, cli->timeout);
+                       }
+                       if (cli->fd != -1) {
+                               cli->port = port;
+                       }
+               }
+               if (cli->fd == -1) {
+                       char addr[INET6_ADDRSTRLEN];
+                       print_sockaddr(addr, sizeof(addr), &ss_arr[i]);
+                       DEBUG(2,("Error connecting to %s (%s)\n",
+                                dest_ss?addr:host,strerror(errno)));
+               } else {
+                       /* Exit from loop on first connection. */
+                       break;
                }
-               if (cli->fd != -1)
-                       cli->port = port;
        }
+
        if (cli->fd == -1) {
-               DEBUG(1,("Error connecting to %s (%s)\n",
-                        ip?inet_ntoa(*ip):host,strerror(errno)));
-               return False;
+               TALLOC_FREE(frame);
+               return map_nt_error_from_unix(errno);
        }
 
-       set_socket_options(cli->fd,user_socket_options);
+       if (dest_ss) {
+               *dest_ss = cli->dest_ss;
+       }
 
-       return True;
+       set_socket_options(cli->fd, lp_socket_options());
+
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
 }
 
 /**
    establishes a connection to after the negprot. 
    @param output_cli A fully initialised cli structure, non-null only on success
    @param dest_host The netbios name of the remote host
-   @param dest_ip (optional) The the destination IP, NULL for name based lookup
+   @param dest_ss (optional) The the destination IP, NULL for name based lookup
    @param port (optional) The destination port (0 for default)
-   @param retry BOOL. Did this connection fail with a retryable error ?
+   @param retry bool. Did this connection fail with a retryable error ?
 
 */
 NTSTATUS cli_start_connection(struct cli_state **output_cli, 
                              const char *my_name, 
                              const char *dest_host, 
-                             struct in_addr *dest_ip, int port,
+                             struct sockaddr_storage *dest_ss, int port,
                              int signing_state, int flags,
-                             BOOL *retry) 
+                             bool *retry) 
 {
        NTSTATUS nt_status;
        struct nmb_name calling;
        struct nmb_name called;
        struct cli_state *cli;
-       struct in_addr ip;
+       struct sockaddr_storage ss;
 
        if (retry)
                *retry = False;
@@ -1384,8 +1584,9 @@ NTSTATUS cli_start_connection(struct cli_state **output_cli,
        if (!my_name) 
                my_name = global_myname();
        
-       if (!(cli = cli_initialise(NULL)))
+       if (!(cli = cli_initialise())) {
                return NT_STATUS_NO_MEMORY;
+       }
        
        make_nmb_name(&calling, my_name, 0x0);
        make_nmb_name(&called , dest_host, 0x20);
@@ -1397,24 +1598,24 @@ NTSTATUS cli_start_connection(struct cli_state **output_cli,
 
        cli_set_timeout(cli, 10000); /* 10 seconds. */
 
-       if (dest_ip)
-               ip = *dest_ip;
-       else
-               ZERO_STRUCT(ip);
+       if (dest_ss) {
+               ss = *dest_ss;
+       } else {
+               zero_addr(&ss);
+       }
 
 again:
 
        DEBUG(3,("Connecting to host=%s\n", dest_host));
-       
-       if (!cli_connect(cli, dest_host, &ip)) {
-               DEBUG(1,("cli_start_connection: failed to connect to %s (%s)\n",
-                        nmb_namestr(&called), inet_ntoa(ip)));
+
+       nt_status = cli_connect(cli, dest_host, &ss);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               char addr[INET6_ADDRSTRLEN];
+               print_sockaddr(addr, sizeof(addr), &ss);
+               DEBUG(1,("cli_start_connection: failed to connect to %s (%s). Error %s\n",
+                        nmb_namestr(&called), addr, nt_errstr(nt_status) ));
                cli_shutdown(cli);
-               if (is_zero_ip(ip)) {
-                       return NT_STATUS_BAD_NETWORK_NAME;
-               } else {
-                       return NT_STATUS_CONNECTION_REFUSED;
-               }
+               return nt_status;
        }
 
        if (retry)
@@ -1422,14 +1623,14 @@ again:
 
        if (!cli_session_request(cli, &calling, &called)) {
                char *p;
-               DEBUG(1,("session request to %s failed (%s)\n", 
+               DEBUG(1,("session request to %s failed (%s)\n",
                         called.name, cli_errstr(cli)));
                if ((p=strchr(called.name, '.')) && !is_ipaddress(called.name)) {
                        *p = 0;
                        goto again;
                }
-               if (strcmp(called.name, "*SMBSERVER")) {
-                       make_nmb_name(&called , "*SMBSERVER", 0x20);
+               if (strcmp(called.name, star_smbserver_name)) {
+                       make_nmb_name(&called , star_smbserver_name, 0x20);
                        goto again;
                }
                return NT_STATUS_BAD_NETWORK_NAME;
@@ -1468,47 +1669,59 @@ again:
    @param user Username, unix string
    @param domain User's domain
    @param password User's password, unencrypted unix string.
-   @param retry BOOL. Did this connection fail with a retryable error ?
+   @param retry bool. Did this connection fail with a retryable error ?
 */
 
 NTSTATUS cli_full_connection(struct cli_state **output_cli, 
                             const char *my_name, 
                             const char *dest_host, 
-                            struct in_addr *dest_ip, int port,
+                            struct sockaddr_storage *dest_ss, int port,
                             const char *service, const char *service_type,
                             const char *user, const char *domain, 
                             const char *password, int flags,
                             int signing_state,
-                            BOOL *retry) 
+                            bool *retry) 
 {
        NTSTATUS nt_status;
        struct cli_state *cli = NULL;
+       int pw_len = password ? strlen(password)+1 : 0;
+
+       *output_cli = NULL;
+
+       if (password == NULL) {
+               password = "";
+       }
+
+       nt_status = cli_start_connection(&cli, my_name, dest_host,
+                                        dest_ss, port, signing_state,
+                                        flags, retry);
 
-       nt_status = cli_start_connection(&cli, my_name, dest_host, 
-                                        dest_ip, port, signing_state, flags, retry);
-       
        if (!NT_STATUS_IS_OK(nt_status)) {
                return nt_status;
        }
 
-       if (!cli_session_setup(cli, user, password, strlen(password)+1, 
-                              password, strlen(password)+1, 
-                              domain)) {
-               if ((flags & CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK)
-                   && cli_session_setup(cli, "", "", 0, "", 0, domain)) {
-               } else {
-                       nt_status = cli_nt_error(cli);
-                       DEBUG(1,("failed session setup with %s\n", nt_errstr(nt_status)));
+       nt_status = cli_session_setup(cli, user, password, pw_len, password,
+                                     pw_len, domain);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+
+               if (!(flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK)) {
+                       DEBUG(1,("failed session setup with %s\n",
+                                nt_errstr(nt_status)));
                        cli_shutdown(cli);
-                       if (NT_STATUS_IS_OK(nt_status)) 
-                               nt_status = NT_STATUS_UNSUCCESSFUL;
                        return nt_status;
                }
-       } 
+
+               nt_status = cli_session_setup(cli, "", "", 0, "", 0, domain);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       DEBUG(1,("anonymous failed session setup with %s\n",
+                                nt_errstr(nt_status)));
+                       cli_shutdown(cli);
+                       return nt_status;
+               }
+       }
 
        if (service) {
-               if (!cli_send_tconX(cli, service, service_type,
-                                   password, strlen(password)+1)) {
+               if (!cli_send_tconX(cli, service, service_type, password, pw_len)) {
                        nt_status = cli_nt_error(cli);
                        DEBUG(1,("failed tcon_X with %s\n", nt_errstr(nt_status)));
                        cli_shutdown(cli);
@@ -1529,8 +1742,8 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli,
  Attempt a NetBIOS session request, falling back to *SMBSERVER if needed.
 ****************************************************************************/
 
-BOOL attempt_netbios_session_request(struct cli_state *cli, const char *srchost, const char *desthost,
-                                     struct in_addr *pdest_ip)
+bool attempt_netbios_session_request(struct cli_state **ppcli, const char *srchost, const char *desthost,
+                                     struct sockaddr_storage *pdest_ss)
 {
        struct nmb_name calling, called;
 
@@ -1541,15 +1754,17 @@ BOOL attempt_netbios_session_request(struct cli_state *cli, const char *srchost,
         * then use *SMBSERVER immediately.
         */
 
-       if(is_ipaddress(desthost))
-               make_nmb_name(&called, "*SMBSERVER", 0x20);
-       else
+       if(is_ipaddress(desthost)) {
+               make_nmb_name(&called, star_smbserver_name, 0x20);
+       } else {
                make_nmb_name(&called, desthost, 0x20);
+       }
 
-       if (!cli_session_request(cli, &calling, &called)) {
+       if (!cli_session_request(*ppcli, &calling, &called)) {
+               NTSTATUS status;
                struct nmb_name smbservername;
 
-               make_nmb_name(&smbservername , "*SMBSERVER", 0x20);
+               make_nmb_name(&smbservername, star_smbserver_name, 0x20);
 
                /*
                 * If the name wasn't *SMBSERVER then
@@ -1563,23 +1778,24 @@ BOOL attempt_netbios_session_request(struct cli_state *cli, const char *srchost,
                         */
 
                        DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \
-with error %s.\n", desthost, cli_errstr(cli) ));
+with error %s.\n", desthost, cli_errstr(*ppcli) ));
                        return False;
                }
 
-               /*
-                * We need to close the connection here but can't call cli_shutdown as
-                * will free an allocated cli struct. cli_close_connection was invented
-                * for this purpose. JRA. Based on work by "Kim R. Pedersen" <krp@filanet.dk>.
-                */
+               /* Try again... */
+               cli_shutdown(*ppcli);
 
-               cli_close_connection(cli);
+               *ppcli = cli_initialise();
+               if (!*ppcli) {
+                       /* Out of memory... */
+                       return False;
+               }
 
-               if (!cli_initialise(cli) ||
-                               !cli_connect(cli, desthost, pdest_ip) ||
-                               !cli_session_request(cli, &calling, &smbservername)) {
+               status = cli_connect(*ppcli, desthost, pdest_ss);
+               if (!NT_STATUS_IS_OK(status) ||
+                               !cli_session_request(*ppcli, &calling, &smbservername)) {
                        DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
-name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));
+name *SMBSERVER with error %s\n", desthost, cli_errstr(*ppcli) ));
                        return False;
                }
        }
@@ -1587,10 +1803,6 @@ name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));
        return True;
 }
 
-
-
-
-
 /****************************************************************************
  Send an old style tcon.
 ****************************************************************************/
@@ -1601,15 +1813,15 @@ NTSTATUS cli_raw_tcon(struct cli_state *cli,
        char *p;
 
        if (!lp_client_plaintext_auth() && (*pass)) {
-               DEBUG(1, ("Server requested plaintext password but 'client use plaintext auth'"
-                         " is disabled\n"));
+               DEBUG(1, ("Server requested plaintext password but 'client "
+                         "plaintext auth' is disabled\n"));
                return NT_STATUS_ACCESS_DENIED;
        }
 
        memset(cli->outbuf,'\0',smb_size);
        memset(cli->inbuf,'\0',smb_size);
 
-       set_message(cli->outbuf, 0, 0, True);
+       cli_set_message(cli->outbuf, 0, 0, True);
        SCVAL(cli->outbuf,smb_com,SMBtcon);
        cli_setup_packet(cli);
 
@@ -1637,18 +1849,18 @@ NTSTATUS cli_raw_tcon(struct cli_state *cli,
 
 /* Return a cli_state pointing at the IPC$ share for the given server */
 
-struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip,
-                                         struct user_auth_info *user_info)
+struct cli_state *get_ipc_connect(char *server,
+                               struct sockaddr_storage *server_ss,
+                               const struct user_auth_info *user_info)
 {
         struct cli_state *cli;
-        pstring myname;
        NTSTATUS nt_status;
 
-        get_myname(myname);
-       
-       nt_status = cli_full_connection(&cli, myname, server, server_ip, 0, "IPC$", "IPC", 
-                                       user_info->username, lp_workgroup(), user_info->password, 
-                                       CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, Undefined, NULL);
+       nt_status = cli_full_connection(&cli, NULL, server, server_ss, 0, "IPC$", "IPC", 
+                                       user_info->username ? user_info->username : "",
+                                       lp_workgroup(),
+                                       user_info->password ? user_info->password : "",
+                                       CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK, Undefined, NULL);
 
        if (NT_STATUS_IS_OK(nt_status)) {
                return cli;
@@ -1656,8 +1868,8 @@ struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip,
            /* windows 9* needs a correct NMB name for connections */
            fstring remote_name;
 
-           if (name_status_find("*", 0, 0, *server_ip, remote_name)) {
-               cli = get_ipc_connect(remote_name, server_ip, user_info);
+           if (name_status_find("*", 0, 0, server_ss, remote_name)) {
+               cli = get_ipc_connect(remote_name, server_ss, user_info);
                if (cli)
                    return cli;
            }
@@ -1677,14 +1889,21 @@ struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip,
  * entire network browse list)
  */
 
-struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring workgroup, struct user_auth_info *user_info)
+struct cli_state *get_ipc_connect_master_ip(TALLOC_CTX *ctx,
+                               struct ip_service *mb_ip,
+                               const struct user_auth_info *user_info,
+                               char **pp_workgroup_out)
 {
-        static fstring name;
+       char addr[INET6_ADDRSTRLEN];
+        fstring name;
        struct cli_state *cli;
-       struct in_addr server_ip; 
+       struct sockaddr_storage server_ss;
 
+       *pp_workgroup_out = NULL;
+
+       print_sockaddr(addr, sizeof(addr), &mb_ip->ss);
         DEBUG(99, ("Looking up name of master browser %s\n",
-                   inet_ntoa(mb_ip->ip)));
+                   addr));
 
         /*
          * Do a name status query to find out the name of the master browser.
@@ -1697,28 +1916,27 @@ struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring w
          * the original wildcard query as the first choice and fall back to
          * MSBROWSE if the wildcard query fails.
          */
-        if (!name_status_find("*", 0, 0x1d, mb_ip->ip, name) &&
-            !name_status_find(MSBROWSE, 1, 0x1d, mb_ip->ip, name)) {
+        if (!name_status_find("*", 0, 0x1d, &mb_ip->ss, name) &&
+            !name_status_find(MSBROWSE, 1, 0x1d, &mb_ip->ss, name)) {
 
                 DEBUG(99, ("Could not retrieve name status for %s\n",
-                           inet_ntoa(mb_ip->ip)));
+                           addr));
                 return NULL;
         }
 
-        if (!find_master_ip(name, &server_ip)) {
+        if (!find_master_ip(name, &server_ss)) {
                 DEBUG(99, ("Could not find master ip for %s\n", name));
                 return NULL;
         }
 
-                pstrcpy(workgroup, name);
+       *pp_workgroup_out = talloc_strdup(ctx, name);
 
-                DEBUG(4, ("found master browser %s, %s\n", 
-                  name, inet_ntoa(mb_ip->ip)));
+       DEBUG(4, ("found master browser %s, %s\n", name, addr));
 
-               cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info);
+       print_sockaddr(addr, sizeof(addr), &server_ss);
+       cli = get_ipc_connect(addr, &server_ss, user_info);
 
-               return cli;
-    
+       return cli;
 }
 
 /*
@@ -1726,27 +1944,35 @@ struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring w
  * connect to it.
  */
 
-struct cli_state *get_ipc_connect_master_ip_bcast(pstring workgroup, struct user_auth_info *user_info)
+struct cli_state *get_ipc_connect_master_ip_bcast(TALLOC_CTX *ctx,
+                                       const struct user_auth_info *user_info,
+                                       char **pp_workgroup_out)
 {
        struct ip_service *ip_list;
        struct cli_state *cli;
        int i, count;
 
+       *pp_workgroup_out = NULL;
+
         DEBUG(99, ("Do broadcast lookup for workgroups on local network\n"));
 
-        /* Go looking for workgroups by broadcasting on the local network */ 
+        /* Go looking for workgroups by broadcasting on the local network */
 
-        if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+        if (!NT_STATUS_IS_OK(name_resolve_bcast(MSBROWSE, 1, &ip_list,
+                                               &count))) {
                 DEBUG(99, ("No master browsers responded\n"));
                 return False;
         }
 
        for (i = 0; i < count; i++) {
-            DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip)));
+               char addr[INET6_ADDRSTRLEN];
+               print_sockaddr(addr, sizeof(addr), &ip_list[i].ss);
+               DEBUG(99, ("Found master browser %s\n", addr));
 
-            cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, user_info);
-            if (cli)
-                    return(cli);
+               cli = get_ipc_connect_master_ip(ctx, &ip_list[i],
+                               user_info, pp_workgroup_out);
+               if (cli)
+                       return(cli);
        }
 
        return NULL;