Add the strlen to push to smb_bytes_push_str, return the converted size
[metze/samba/wip.git] / source3 / libsmb / cliconnect.c
index f13aa21fbdd88c0f5867c5f9a12ec0fd1590eff4..5778e7f6a39b3d68de446d5704e8fbb4d256d9d0 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;
-} prots[] = {
-       {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
-       {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
-       {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
-       {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}
+       const char name[24];
+} prots[10] = {
+       {PROTOCOL_CORE,         "PC NETWORK PROGRAM 1.0"},
+       {PROTOCOL_COREPLUS,     "MICROSOFT NETWORKS 1.03"},
+       {PROTOCOL_LANMAN1,      "MICROSOFT NETWORKS 3.0"},
+       {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"},
 };
 
+#define STAR_SMBSERVER "*SMBSERVER"
+
 /**
  * Set the user session key for a connection
  * @param cli The cli structure to add it too
@@ -99,7 +97,7 @@ static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli,
 
        /* send a session setup command */
        memset(cli->outbuf,'\0',smb_size);
-       set_message(NULL,cli->outbuf,10, 0, True);
+       cli_set_message(cli->outbuf,10, 0, True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
        
@@ -169,7 +167,7 @@ static NTSTATUS cli_session_setup_guest(struct cli_state *cli)
        uint32 capabilities = cli_session_setup_capabilities(cli);
 
        memset(cli->outbuf, '\0', smb_size);
-       set_message(NULL,cli->outbuf,13,0,True);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -201,9 +199,12 @@ static NTSTATUS cli_session_setup_guest(struct cli_state *cli)
        cli->vuid = SVAL(cli->inbuf,smb_uid);
 
        p = smb_buf(cli->inbuf);
-       p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
-       p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
-       p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+       p += clistr_pull(cli->inbuf, cli->server_os, p, sizeof(fstring),
+                        -1, STR_TERMINATE);
+       p += clistr_pull(cli->inbuf, cli->server_type, p, sizeof(fstring),
+                        -1, STR_TERMINATE);
+       p += clistr_pull(cli->inbuf, cli->server_domain, p, sizeof(fstring),
+                        -1, STR_TERMINATE);
 
        if (strstr(cli->server_type, "Samba")) {
                cli->is_samba = True;
@@ -226,10 +227,10 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
        char *p;
        fstring lanman;
        
-       fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
+       fstr_sprintf( lanman, "Samba %s", samba_version_string());
 
        memset(cli->outbuf, '\0', smb_size);
-       set_message(NULL,cli->outbuf,13,0,True);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -248,9 +249,16 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
                p += clistr_push(cli, p, pass, -1, STR_TERMINATE); /* password */
                SSVAL(cli->outbuf,smb_vwv7,PTR_DIFF(p, smb_buf(cli->outbuf)));
        }
-       else { 
+       else {
+               /* For ucs2 passwords clistr_push calls ucs2_align, which causes
+                * the space taken by the unicode password to be one byte too
+                * long (as we're on an odd byte boundary here). Reduce the
+                * count by 1 to cope with this. Fixes smbclient against NetApp
+                * servers which can't cope. Fix from
+                * bryan.kolodziej@allenlund.com in bug #3840.
+                */
                p += clistr_push(cli, p, pass, -1, STR_UNICODE|STR_TERMINATE); /* unicode password */
-               SSVAL(cli->outbuf,smb_vwv8,PTR_DIFF(p, smb_buf(cli->outbuf)));  
+               SSVAL(cli->outbuf,smb_vwv8,PTR_DIFF(p, smb_buf(cli->outbuf))-1);        
        }
        
        p += clistr_push(cli, p, user, -1, STR_TERMINATE); /* username */
@@ -271,9 +279,12 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
 
        cli->vuid = SVAL(cli->inbuf,smb_uid);
        p = smb_buf(cli->inbuf);
-       p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
-       p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
-       p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+       p += clistr_pull(cli->inbuf, cli->server_os, p, sizeof(fstring),
+                        -1, STR_TERMINATE);
+       p += clistr_pull(cli->inbuf, cli->server_type, p, sizeof(fstring),
+                        -1, STR_TERMINATE);
+       p += clistr_pull(cli->inbuf, cli->server_domain, p, sizeof(fstring),
+                        -1, STR_TERMINATE);
        fstrcpy(cli->user_name, user);
 
        if (strstr(cli->server_type, "Samba")) {
@@ -378,7 +389,7 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user,
        /* send a session setup command */
        memset(cli->outbuf,'\0',smb_size);
 
-       set_message(NULL,cli->outbuf,13,0,True);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -421,9 +432,12 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user,
        cli->vuid = SVAL(cli->inbuf,smb_uid);
        
        p = smb_buf(cli->inbuf);
-       p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
-       p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
-       p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+       p += clistr_pull(cli->inbuf, cli->server_os, p, sizeof(fstring),
+                        -1, STR_TERMINATE);
+       p += clistr_pull(cli->inbuf, cli->server_type, p, sizeof(fstring),
+                        -1, STR_TERMINATE);
+       p += clistr_pull(cli->inbuf, cli->server_domain, p, sizeof(fstring),
+                        -1, STR_TERMINATE);
 
        if (strstr(cli->server_type, "Samba")) {
                cli->is_samba = True;
@@ -448,7 +462,7 @@ end:
  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;
@@ -458,11 +472,11 @@ 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(NULL,cli->outbuf,12,0,True);
+       cli_set_message(cli->outbuf,12,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
 
        cli_setup_packet(cli);
-                       
+
        SCVAL(cli->outbuf,smb_vwv0,0xFF);
        SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
        SSVAL(cli->outbuf,smb_vwv3,2);
@@ -498,20 +512,22 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
                                                  NT_STATUS_MORE_PROCESSING_REQUIRED)) {
                return blob2;
        }
-       
+
        /* use the returned vuid from now on */
        cli->vuid = SVAL(cli->inbuf,smb_uid);
-       
+
        p = smb_buf(cli->inbuf);
 
        blob2 = data_blob(p, SVAL(cli->inbuf, smb_vwv3));
 
        p += blob2.length;
-       p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+       p += clistr_pull(cli->inbuf, cli->server_os, p, sizeof(fstring),
+                        -1, STR_TERMINATE);
 
        /* w2k with kerberos doesn't properly null terminate this field */
-       len = smb_buflen(cli->inbuf) - PTR_DIFF(p, smb_buf(cli->inbuf));
-       p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), len, 0);
+       len = smb_bufrem(cli->inbuf, p);
+       p += clistr_pull(cli->inbuf, cli->server_type, p, sizeof(fstring),
+                        len, 0);
 
        return blob2;
 }
@@ -530,7 +546,7 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
 
 #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)
+static bool cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
 {
        int32 remaining = blob.length;
        int32 cur = 0;
@@ -554,13 +570,8 @@ static BOOL cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob, DATA_B
                        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];
@@ -582,8 +593,9 @@ static BOOL cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob, DATA_B
                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)) ));
+                       DEBUG(0, ("cli_session_setup_blob: receive failed "
+                                 "(%s)\n", nt_errstr(cli_get_nt_error(cli))));
+                       cli->vuid = 0;
                        return False;
                }
        }
@@ -607,8 +619,11 @@ static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *
 {
        DATA_BLOB negTokenTarg;
        DATA_BLOB session_key_krb5;
+       NTSTATUS nt_status;
        int rc;
 
+       cli_temp_set_signing(cli);
+
        DEBUG(2,("Doing kerberos session setup\n"));
 
        /* generate the encapsulated kerberos5 ticket */
@@ -624,23 +639,44 @@ static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *
        file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length);
 #endif
 
-       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));
+       if (!cli_session_setup_blob(cli, negTokenTarg)) {
+               nt_status = cli_nt_error(cli);
+               goto nt_error;
+       }
+
+       if (cli_is_error(cli)) {
+               nt_status = cli_nt_error(cli);
+               if (NT_STATUS_IS_OK(nt_status)) {
+                       nt_status = NT_STATUS_UNSUCCESSFUL;
+               }
+               goto nt_error;
        }
 
        cli_set_session_key(cli, session_key_krb5);
 
+       if (cli_simple_set_signing(
+                   cli, session_key_krb5, data_blob_null)) {
+
+               /* 'resign' the last message, so we get the right sequence numbers
+                  for checking the first reply from the server */
+               cli_calculate_sign_mac(cli, cli->outbuf);
+
+               if (!cli_check_sign_mac(cli, cli->inbuf)) {
+                       nt_status = NT_STATUS_ACCESS_DENIED;
+                       goto nt_error;
+               }
+       }
+
        data_blob_free(&negTokenTarg);
        data_blob_free(&session_key_krb5);
 
-       if (cli_is_error(cli)) {
-               if (NT_STATUS_IS_OK(cli_nt_error(cli))) {
-                       return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
-               }
-       } 
-       return ADS_ERROR_NT(cli_nt_error(cli));
+       return ADS_ERROR_NT(NT_STATUS_OK);
+
+nt_error:
+       data_blob_free(&negTokenTarg);
+       data_blob_free(&session_key_krb5);
+       cli->vuid = 0;
+       return ADS_ERROR_NT(nt_status);
 }
 #endif /* HAVE_KRB5 */
 
@@ -689,17 +725,17 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                                /* wrap it in SPNEGO */
                                msg1 = spnego_gen_auth(blob_out);
                        }
-               
+
                        /* now send that blob on its way */
                        if (!cli_session_setup_blob_send(cli, msg1)) {
                                DEBUG(3, ("Failed to send NTLMSSP/SPNEGO blob to server!\n"));
                                nt_status = NT_STATUS_UNSUCCESSFUL;
                        } else {
                                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;
@@ -708,7 +744,7 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                        }
                        data_blob_free(&msg1);
                }
-               
+
                if (!blob.length) {
                        if (NT_STATUS_IS_OK(nt_status)) {
                                nt_status = NT_STATUS_UNSUCCESSFUL;
@@ -741,50 +777,50 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
 
        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;
-               BOOL res;
-
                fstrcpy(cli->server_domain, ntlmssp_state->server_domain);
                cli_set_session_key(cli, ntlmssp_state->session_key);
 
-               res = cli_simple_set_signing(cli, key, null_blob);
-
-               data_blob_free(&key);
+               if (cli_simple_set_signing(
+                           cli, ntlmssp_state->session_key, data_blob_null)) {
 
-               if (res) {
-                       
                        /* 'resign' the last message, so we get the right sequence numbers
                           for checking the first reply from the server */
-                       cli_calculate_sign_mac(cli);
-                       
-                       if (!cli_check_sign_mac(cli)) {
+                       cli_calculate_sign_mac(cli, cli->outbuf);
+
+                       if (!cli_check_sign_mac(cli, cli->inbuf)) {
                                nt_status = NT_STATUS_ACCESS_DENIED;
                        }
                }
        }
 
-       /* we have a reference counter on ntlmssp_state, if we are signing
+       /* we have a reference conter on ntlmssp_state, if we are signing
           then the state will be kept by the signing engine */
 
        ntlmssp_end(&ntlmssp_state);
 
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               cli->vuid = 0;
+       }
        return nt_status;
 }
 
 /****************************************************************************
  Do a spnego encrypted session setup.
+
+ user_domain: The shortname of the domain the user/machine is a member of.
+ dest_realm: The realm we're connecting to, if NULL we use our default realm.
 ****************************************************************************/
 
 ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user, 
-                             const char *pass, const char *domain)
+                             const char *pass, const char *user_domain,
+                             const char * dest_realm)
 {
-       char *principal;
+       char *principal = NULL;
        char *OIDs[ASN1_MAX_OIDS];
        int i;
-       BOOL got_kerberos_mechanism = False;
        DATA_BLOB blob;
+       const char *p = NULL;
+       char *account = NULL;
 
        DEBUG(3,("Doing spnego session setup (blob length=%lu)\n", (unsigned long)cli->secblob.length));
 
@@ -801,8 +837,10 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
        /* there is 16 bytes of GUID before the real spnego packet starts */
        blob = data_blob(cli->secblob.data+16, cli->secblob.length-16);
 
-       /* the server sent us the first part of the SPNEGO exchange in the negprot 
-          reply */
+       /* The server sent us the first part of the SPNEGO exchange in the
+        * negprot reply. It is WRONG to depend on the principal sent in the
+        * negprot reply, but right now we do it. If we don't receive one,
+        * we try to best guess, then fall back to NTLM.  */
        if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
                data_blob_free(&blob);
                return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
@@ -814,62 +852,115 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
                DEBUG(3,("got OID=%s\n", OIDs[i]));
                if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
                    strcmp(OIDs[i], OID_KERBEROS5) == 0) {
-                       got_kerberos_mechanism = True;
+                       cli->got_kerberos_mechanism = True;
                }
-               free(OIDs[i]);
+               talloc_free(OIDs[i]);
        }
 
        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);
 
 #ifdef HAVE_KRB5
        /* If password is set we reauthenticate to kerberos server
         * and do not store results */
 
-       if (got_kerberos_mechanism && cli->use_kerberos) {
+       if (cli->got_kerberos_mechanism && cli->use_kerberos) {
                ADS_STATUS rc;
 
                if (pass && *pass) {
                        int ret;
-                       
+
                        use_in_memory_ccache();
                        ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */, NULL);
-                       
+
                        if (ret){
-                               SAFE_FREE(principal);
+                               TALLOC_FREE(principal);
                                DEBUG(0, ("Kinit failed: %s\n", error_message(ret)));
                                if (cli->fallback_after_kerberos)
                                        goto ntlmssp;
                                return ADS_ERROR_KRB5(ret);
                        }
                }
-               
-               rc = cli_session_setup_kerberos(cli, principal, domain);
-               if (ADS_ERR_OK(rc) || !cli->fallback_after_kerberos) {
-                       SAFE_FREE(principal);
-                       return rc;
+
+               /* If we get a bad principal, try to guess it if
+                  we have a valid host NetBIOS name.
+                */
+               if (strequal(principal, ADS_IGNORE_PRINCIPAL)) {
+                       TALLOC_FREE(principal);
+               }
+
+               if (principal == NULL &&
+                       !is_ipaddress(cli->desthost) &&
+                       !strequal(STAR_SMBSERVER,
+                               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);
+                       }
+
+                       if (dest_realm) {
+                               realm = SMB_STRDUP(dest_realm);
+                               strupper_m(realm);
+                       } else {
+                               realm = kerberos_get_default_realm_from_ccache();
+                       }
+                       if (realm && *realm) {
+                               principal = talloc_asprintf(NULL, "%s$@%s",
+                                                       machine, realm);
+                               if (!principal) {
+                                       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,
+                               dest_realm);
+                       if (ADS_ERR_OK(rc) || !cli->fallback_after_kerberos) {
+                               TALLOC_FREE(principal);
+                               return rc;
+                       }
                }
        }
 #endif
 
-       SAFE_FREE(principal);
+       TALLOC_FREE(principal);
 
 ntlmssp:
 
-       return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, user, pass, domain));
+       account = talloc_strdup(talloc_tos(), user);
+       if (!account) {
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       /* when falling back to ntlmssp while authenticating with a machine
+        * account strip off the realm - gd */
+
+       if ((p = strchr_m(user, '@')) != NULL) {
+               account[PTR_DIFF(p,user)] = '\0';
+       }
+
+       return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, account, pass, user_domain));
 }
 
 /****************************************************************************
@@ -878,8 +969,8 @@ ntlmssp:
  password is in plaintext, the same should be done.
 ****************************************************************************/
 
-NTSTATUS cli_session_setup(struct cli_state *cli, 
-                          const char *user, 
+NTSTATUS cli_session_setup(struct cli_state *cli,
+                          const char *user,
                           const char *pass, int passlen,
                           const char *ntpass, int ntpasslen,
                           const char *workgroup)
@@ -887,8 +978,17 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
        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;
@@ -915,8 +1015,8 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
 
                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"));
+                       DEBUG(1, ("Server requested plaintext password but "
+                                 "'client plaintext auth' is disabled\n"));
                        return NT_STATUS_ACCESS_DENIED;
                }
 
@@ -942,8 +1042,8 @@ NTSTATUS 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"));
+                       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);
@@ -952,7 +1052,8 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
        /* if the server supports extended security then use SPNEGO */
 
        if (cli->capabilities & CAP_EXTENDED_SECURITY) {
-               ADS_STATUS status = cli_session_setup_spnego(cli, user, pass, workgroup);
+               ADS_STATUS status = cli_session_setup_spnego(cli, user, pass,
+                                                            workgroup, NULL);
                if (!ADS_ERR_OK(status)) {
                        DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
                        return ads_ntstatus(status);
@@ -981,10 +1082,10 @@ NTSTATUS 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(NULL,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);
@@ -1006,7 +1107,7 @@ 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;
@@ -1028,8 +1129,9 @@ BOOL cli_send_tconX(struct cli_state *cli,
        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"));
+                       DEBUG(1, ("Server requested LANMAN password "
+                                 "(share-level security) but "
+                                 "'client lanman auth' is disabled\n"));
                        return False;
                }
 
@@ -1041,8 +1143,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;
                        }
 
@@ -1050,7 +1153,7 @@ BOOL cli_send_tconX(struct cli_state *cli,
                         * Non-encrypted passwords - convert to DOS codepage before using.
                         */
                        passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
-                       
+
                } else {
                        if (passlen) {
                                memcpy(pword, pass, passlen);
@@ -1061,7 +1164,7 @@ BOOL cli_send_tconX(struct cli_state *cli,
        slprintf(fullshare, sizeof(fullshare)-1,
                 "\\\\%s\\%s", cli->desthost, share);
 
-       set_message(NULL,cli->outbuf,4, 0, True);
+       cli_set_message(cli->outbuf,4, 0, True);
        SCVAL(cli->outbuf,smb_com,SMBtconX);
        cli_setup_packet(cli);
 
@@ -1086,14 +1189,15 @@ BOOL cli_send_tconX(struct cli_state *cli,
        if (cli_is_error(cli))
                return False;
 
-       clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, STR_TERMINATE|STR_ASCII);
+       clistr_pull(cli->inbuf, cli->dev, smb_buf(cli->inbuf), sizeof(fstring),
+                   -1, STR_TERMINATE|STR_ASCII);
 
        if (cli->protocol >= PROTOCOL_NT1 &&
            smb_buflen(cli->inbuf) == 3) {
                /* almost certainly win95 - enable bug fixes */
                cli->win95 = True;
        }
-       
+
        /* Make sure that we have the optional support 16-bit field.  WCT > 2 */
        /* Avoids issues when connecting to Win9x boxes sharing files */
 
@@ -1109,18 +1213,18 @@ 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(NULL,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);
-       
+
        cli_send_smb(cli);
        if (!cli_receive_smb(cli))
                return False;
-       
+
        if (cli_is_error(cli)) {
                return False;
        }
@@ -1133,7 +1237,7 @@ BOOL cli_tdis(struct cli_state *cli)
  Send a negprot command.
 ****************************************************************************/
 
-void cli_negprot_send(struct cli_state *cli)
+void cli_negprot_sendsync(struct cli_state *cli)
 {
        char *p;
        int numprots;
@@ -1144,12 +1248,13 @@ void cli_negprot_send(struct cli_state *cli)
        memset(cli->outbuf,'\0',smb_size);
 
        /* setup the protocol strings */
-       set_message(NULL,cli->outbuf,0,0,True);
+       cli_set_message(cli->outbuf,0,0,True);
 
        p = smb_buf(cli->outbuf);
-       for (numprots=0;
-            prots[numprots].name && prots[numprots].prot<=cli->protocol;
-            numprots++) {
+       for (numprots=0; numprots < ARRAY_SIZE(prots); numprots++) {
+               if (prots[numprots].prot > cli->protocol) {
+                       break;
+               }
                *p++ = 2;
                p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
        }
@@ -1167,70 +1272,92 @@ void cli_negprot_send(struct cli_state *cli)
  Send a negprot command.
 ****************************************************************************/
 
-BOOL cli_negprot(struct cli_state *cli)
+struct async_req *cli_negprot_send(TALLOC_CTX *mem_ctx,
+                                  struct event_context *ev,
+                                  struct cli_state *cli)
 {
-       char *p;
+       struct async_req *result;
+       uint8_t *bytes = NULL;
        int numprots;
-       int plength;
 
        if (cli->protocol < PROTOCOL_NT1)
                cli->use_spnego = False;
 
-       memset(cli->outbuf,'\0',smb_size);
-
        /* setup the protocol strings */
-       for (plength=0,numprots=0;
-            prots[numprots].name && prots[numprots].prot<=cli->protocol;
-            numprots++)
-               plength += strlen(prots[numprots].name)+2;
-    
-       set_message(NULL,cli->outbuf,0,plength,True);
-
-       p = smb_buf(cli->outbuf);
-       for (numprots=0;
-            prots[numprots].name && prots[numprots].prot<=cli->protocol;
-            numprots++) {
-               *p++ = 2;
-               p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
+       for (numprots=0; numprots < ARRAY_SIZE(prots); numprots++) {
+               uint8_t c = 2;
+               if (prots[numprots].prot > cli->protocol) {
+                       break;
+               }
+               bytes = (uint8_t *)talloc_append_blob(
+                       talloc_tos(), bytes, data_blob_const(&c, sizeof(c)));
+               if (bytes == NULL) {
+                       return NULL;
+               }
+               bytes = smb_bytes_push_str(bytes, false,
+                                          prots[numprots].name,
+                                          strlen(prots[numprots].name)+1,
+                                          NULL);
+               if (bytes == NULL) {
+                       return NULL;
+               }
        }
 
-       SCVAL(cli->outbuf,smb_com,SMBnegprot);
-       cli_setup_packet(cli);
+       result = cli_request_send(mem_ctx, ev, cli, SMBnegprot, 0, 0, NULL, 0,
+                                 talloc_get_size(bytes), bytes);
+       TALLOC_FREE(bytes);
+       return result;
+}
 
-       SCVAL(smb_buf(cli->outbuf),0,2);
+NTSTATUS cli_negprot_recv(struct async_req *req)
+{
+       struct cli_request *cli_req = talloc_get_type_abort(
+               req->private_data, struct cli_request);
+       struct cli_state *cli = cli_req->cli;
+       uint8_t wct;
+       uint16_t *vwv;
+       uint16_t num_bytes;
+       uint8_t *bytes;
+       NTSTATUS status;
+       uint16_t protnum;
 
-       cli_send_smb(cli);
-       if (!cli_receive_smb(cli))
-               return False;
+       if (async_req_is_error(req, &status)) {
+               return status;
+       }
 
-       show_msg(cli->inbuf);
+       status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-       if (cli_is_error(cli) ||
-           ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) {
-               return(False);
+       protnum = SVAL(vwv, 0);
+
+       if ((protnum >= ARRAY_SIZE(prots))
+           || (prots[protnum].prot > cli_req->cli->protocol)) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
        }
 
-       cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;  
+       cli->protocol = prots[protnum].prot;
 
        if ((cli->protocol < PROTOCOL_NT1) && cli->sign_info.mandatory_signing) {
                DEBUG(0,("cli_negprot: SMB signing is mandatory and the selected protocol level doesn't support it.\n"));
-               return False;
+               return NT_STATUS_ACCESS_DENIED;
        }
 
        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);
-               cli->max_xmit = IVAL(cli->inbuf,smb_vwv3+1);
-               cli->sesskey = IVAL(cli->inbuf,smb_vwv7+1);
-               cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1);
+               cli->sec_mode = CVAL(vwv + 1, 0);
+               cli->max_mux = SVAL(vwv + 1, 1);
+               cli->max_xmit = IVAL(vwv + 3, 1);
+               cli->sesskey = IVAL(vwv + 7, 1);
+               cli->serverzone = SVALS(vwv + 15, 1);
                cli->serverzone *= 60;
                /* this time arrives in real GMT */
-               ts = interpret_long_date(cli->inbuf+smb_vwv11+1);
+               ts = interpret_long_date(((char *)(vwv+11))+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);
+               cli->secblob = data_blob(bytes, num_bytes);
+               cli->capabilities = IVAL(vwv + 9, 1);
                if (cli->capabilities & CAP_RAW_MODE) {
                        cli->readbraw_supported = True;
                        cli->writebraw_supported = True;      
@@ -1238,9 +1365,10 @@ BOOL cli_negprot(struct cli_state *cli)
                /* work out if they sent us a workgroup */
                if (!(cli->capabilities & CAP_EXTENDED_SECURITY) &&
                    smb_buflen(cli->inbuf) > 8) {
-                       clistr_pull(cli, cli->server_domain, 
-                                   smb_buf(cli->inbuf)+8, sizeof(cli->server_domain),
-                                   smb_buflen(cli->inbuf)-8, STR_UNICODE|STR_NOALIGN);
+                       clistr_pull(cli->inbuf, cli->server_domain,
+                                   bytes+8, sizeof(cli->server_domain),
+                                   num_bytes-8,
+                                   STR_UNICODE|STR_NOALIGN);
                }
 
                /*
@@ -1252,7 +1380,7 @@ BOOL cli_negprot(struct cli_state *cli)
                        /* Fail if server says signing is mandatory and we don't want to support it. */
                        if (!cli->sign_info.allow_smb_signing) {
                                DEBUG(0,("cli_negprot: SMB signing is mandatory and we have disabled it.\n"));
-                               return False;
+                               return NT_STATUS_ACCESS_DENIED;
                        }
                        cli->sign_info.negotiated_smb_signing = True;
                        cli->sign_info.mandatory_signing = True;
@@ -1260,7 +1388,7 @@ BOOL cli_negprot(struct cli_state *cli)
                        /* Fail if client says signing is mandatory and the server doesn't support it. */
                        if (!(cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) {
                                DEBUG(1,("cli_negprot: SMB signing is mandatory and the server doesn't support it.\n"));
-                               return False;
+                               return NT_STATUS_ACCESS_DENIED;
                        }
                        cli->sign_info.negotiated_smb_signing = True;
                        cli->sign_info.mandatory_signing = True;
@@ -1271,24 +1399,25 @@ BOOL cli_negprot(struct cli_state *cli)
                if (cli->capabilities & (CAP_LARGE_READX|CAP_LARGE_WRITEX)) {
                        SAFE_FREE(cli->outbuf);
                        SAFE_FREE(cli->inbuf);
-                       cli->outbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN);
-                       cli->inbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN);
-                       cli->bufsize = CLI_SAMBA_MAX_LARGE_READX_SIZE;
+                       cli->outbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+LARGE_WRITEX_HDR_SIZE+SAFETY_MARGIN);
+                       cli->inbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+LARGE_WRITEX_HDR_SIZE+SAFETY_MARGIN);
+                       cli->bufsize = CLI_SAMBA_MAX_LARGE_READX_SIZE + LARGE_WRITEX_HDR_SIZE;
                }
 
        } else if (cli->protocol >= PROTOCOL_LANMAN1) {
                cli->use_spnego = False;
-               cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
-               cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
-               cli->max_mux = SVAL(cli->inbuf, smb_vwv3); 
-               cli->sesskey = IVAL(cli->inbuf,smb_vwv6);
-               cli->serverzone = SVALS(cli->inbuf,smb_vwv10);
+               cli->sec_mode = SVAL(vwv + 1, 0);
+               cli->max_xmit = SVAL(vwv + 2, 0);
+               cli->max_mux = SVAL(vwv + 3, 0);
+               cli->sesskey = IVAL(vwv + 6, 0);
+               cli->serverzone = SVALS(vwv + 10, 0);
                cli->serverzone *= 60;
                /* this time is converted to GMT by make_unix_date */
-               cli->servertime = cli_make_unix_date(cli,cli->inbuf+smb_vwv8);
-               cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
-               cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
-               cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
+               cli->servertime = cli_make_unix_date(
+                       cli, (char *)(vwv + 8));
+               cli->readbraw_supported = ((SVAL(vwv + 5, 0) & 0x1) != 0);
+               cli->writebraw_supported = ((SVAL(vwv + 5, 0) & 0x2) != 0);
+               cli->secblob = data_blob(bytes, num_bytes);
        } else {
                /* the old core protocol */
                cli->use_spnego = False;
@@ -1302,22 +1431,61 @@ BOOL cli_negprot(struct cli_state *cli)
        if (getenv("CLI_FORCE_ASCII"))
                cli->capabilities &= ~CAP_UNICODE;
 
-       return True;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS cli_negprot(struct cli_state *cli)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct async_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if (cli->fd_event != NULL) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               cli_set_error(cli, NT_STATUS_INVALID_PARAMETER);
+               goto fail;
+       }
+
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+
+       req = cli_negprot_send(frame, ev, cli);
+       if (req == NULL) {
+               goto fail;
+       }
+
+       while (req->state < ASYNC_REQ_DONE) {
+               event_loop_once(ev);
+       }
+
+       status = cli_negprot_recv(req);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
 }
 
 /****************************************************************************
  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;
        int len = 4;
 
+       /* 445 doesn't have session request */
+       if (cli->port == 445)
+               return True;
+
        memcpy(&(cli->calling), calling, sizeof(*calling));
        memcpy(&(cli->called ), called , sizeof(*called ));
-  
+
        /* put in the destination name */
        p = cli->outbuf+len;
        name_mangle(cli->called .name, p, cli->called .name_type);
@@ -1328,10 +1496,6 @@ BOOL cli_session_request(struct cli_state *cli,
        name_mangle(cli->calling.name, p, cli->calling.name_type);
        len += name_len(p);
 
-       /* 445 doesn't have session request */
-       if (cli->port == 445)
-               return True;
-
        /* send a session request (RFC 1002) */
        /* setup the packet length
          * Remove four bytes from the length count, since the length
@@ -1347,7 +1511,7 @@ BOOL cli_session_request(struct cli_state *cli,
        cli_send_smb(cli);
        DEBUG(5,("Sent session request\n"));
 
-       if (!cli_receive_sessionreply(cli))
+       if (!cli_receive_smb(cli))
                return False;
 
        if (CVAL(cli->inbuf,0) == 0x84) {
@@ -1362,22 +1526,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;
+               NTSTATUS status;
+
                /* 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);
-               if (cli->fd == -1)
+               status = open_socket_out(&cli->dest_ss, port,
+                                        LONG_CONNECT_TIMEOUT, &cli->fd);
+               if (!NT_STATUS_IS_OK(status)) {
                        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;
@@ -1397,121 +1567,222 @@ BOOL cli_session_request(struct cli_state *cli,
        return(True);
 }
 
+static void smb_sock_connected(struct async_req *req)
+{
+       int *pfd = (int *)req->async.priv;
+       int fd;
+       NTSTATUS status;
+
+       status = open_socket_out_defer_recv(req, &fd);
+       if (NT_STATUS_IS_OK(status)) {
+               *pfd = fd;
+       }
+}
+
+static NTSTATUS open_smb_socket(const struct sockaddr_storage *pss,
+                               uint16_t *port, int timeout, int *pfd)
+{
+       struct event_context *ev;
+       struct async_req *r139, *r445;
+       int fd139 = -1;
+       int fd445 = -1;
+       NTSTATUS status;
+
+       if (*port != 0) {
+               return open_socket_out(pss, *port, timeout, pfd);
+       }
+
+       ev = event_context_init(talloc_tos());
+       if (ev == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       r445 = open_socket_out_defer_send(ev, ev, timeval_set(0, 0),
+                                         pss, 445, timeout);
+       r139 = open_socket_out_defer_send(ev, ev, timeval_set(0, 3000),
+                                         pss, 139, timeout);
+       if ((r445 == NULL) || (r139 == NULL)) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+       r445->async.fn = smb_sock_connected;
+       r445->async.priv = &fd445;
+       r139->async.fn = smb_sock_connected;
+       r139->async.priv = &fd139;
+
+       while ((fd139 == -1) && (r139->state < ASYNC_REQ_DONE)
+              && (fd445 == -1) && (r445->state < ASYNC_REQ_DONE)) {
+               event_loop_once(ev);
+       }
+
+       if ((fd139 != -1) && (fd445 != -1)) {
+               close(fd139);
+               fd139 = -1;
+       }
+
+       if (fd445 != -1) {
+               *port = 445;
+               *pfd = fd445;
+               status = NT_STATUS_OK;
+               goto done;
+       }
+       if (fd139 != -1) {
+               *port = 139;
+               *pfd = fd139;
+               status = NT_STATUS_OK;
+               goto done;
+       }
+
+       status = open_socket_out_defer_recv(r445, &fd445);
+ done:
+       TALLOC_FREE(ev);
+       return status;
+}
+
 /****************************************************************************
  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;
+       }
 
        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((struct sockaddr *)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, 
-                                                 port, cli->timeout);
+       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 {
+                       uint16_t port = cli->port;
+                       NTSTATUS status;
+                       status = open_smb_socket(&cli->dest_ss, &port,
+                                                cli->timeout, &cli->fd);
+                       if (NT_STATUS_IS_OK(status)) {
+                               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;
 
        if (!my_name) 
                my_name = global_myname();
-       
+
        if (!(cli = cli_initialise())) {
                return NT_STATUS_NO_MEMORY;
        }
-       
+
        make_nmb_name(&calling, my_name, 0x0);
        make_nmb_name(&called , dest_host, 0x20);
 
-       if (cli_set_port(cli, port) != port) {
-               cli_shutdown(cli);
-               return NT_STATUS_UNSUCCESSFUL;
-       }
-
+       cli_set_port(cli, port);
        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_sockaddr(&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)
@@ -1519,14 +1790,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)) {
+                       make_nmb_name(&called , STAR_SMBSERVER, 0x20);
                        goto again;
                }
                return NT_STATUS_BAD_NETWORK_NAME;
@@ -1539,12 +1810,14 @@ again:
        else if (flags & CLI_FULL_CONNECTION_USE_KERBEROS)
                cli->use_kerberos = True;
 
-       if (!cli_negprot(cli)) {
-               DEBUG(1,("failed negprot\n"));
-               nt_status = cli_nt_error(cli);
-               if (NT_STATUS_IS_OK(nt_status)) {
-                       nt_status = NT_STATUS_UNSUCCESSFUL;
-               }
+       if ((flags & CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS) &&
+            cli->use_kerberos) {
+               cli->fallback_after_kerberos = true;
+       }
+
+       nt_status = cli_negprot(cli);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(1, ("failed negprot: %s\n", nt_errstr(nt_status)));
                cli_shutdown(cli);
                return nt_status;
        }
@@ -1565,18 +1838,18 @@ 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;
@@ -1588,9 +1861,10 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli,
                password = "";
        }
 
-       nt_status = cli_start_connection(&cli, my_name, dest_host, 
-                                        dest_ip, port, signing_state, flags, retry);
-       
+       nt_status = cli_start_connection(&cli, my_name, dest_host,
+                                        dest_ss, port, signing_state,
+                                        flags, retry);
+
        if (!NT_STATUS_IS_OK(nt_status)) {
                return nt_status;
        }
@@ -1614,7 +1888,7 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli,
                        return nt_status;
                }
        }
-       
+
        if (service) {
                if (!cli_send_tconX(cli, service, service_type, password, pw_len)) {
                        nt_status = cli_nt_error(cli);
@@ -1637,8 +1911,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 **ppcli, 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;
 
@@ -1650,15 +1924,16 @@ BOOL attempt_netbios_session_request(struct cli_state **ppcli, const char *srcho
         */
 
        if(is_ipaddress(desthost)) {
-               make_nmb_name(&called, "*SMBSERVER", 0x20);
+               make_nmb_name(&called, STAR_SMBSERVER, 0x20);
        } else {
                make_nmb_name(&called, desthost, 0x20);
        }
 
        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, 0x20);
 
                /*
                 * If the name wasn't *SMBSERVER then
@@ -1685,7 +1960,8 @@ with error %s.\n", desthost, cli_errstr(*ppcli) ));
                        return False;
                }
 
-               if (!cli_connect(*ppcli, desthost, pdest_ip) ||
+               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(*ppcli) ));
@@ -1696,10 +1972,6 @@ name *SMBSERVER with error %s\n", desthost, cli_errstr(*ppcli) ));
        return True;
 }
 
-
-
-
-
 /****************************************************************************
  Send an old style tcon.
 ****************************************************************************/
@@ -1710,15 +1982,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(NULL,cli->outbuf, 0, 0, True);
+       cli_set_message(cli->outbuf, 0, 0, True);
        SCVAL(cli->outbuf,smb_com,SMBtcon);
        cli_setup_packet(cli);
 
@@ -1746,18 +2018,24 @@ 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;
+       uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
 
-        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_ANONYMOUS_FALLBACK, Undefined, NULL);
+       if (user_info->use_kerberos) {
+               flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+       }
+
+       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 : "",
+                                       flags,
+                                       Undefined, NULL);
 
        if (NT_STATUS_IS_OK(nt_status)) {
                return cli;
@@ -1765,8 +2043,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;
            }
@@ -1786,14 +2064,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.
@@ -1806,28 +2091,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;
 }
 
 /*
@@ -1835,27 +2119,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;