s3: Change sockaddr util function names for consistency
[samba.git] / source3 / libsmb / cliconnect.c
index f0b03a85cf222f54cfaf5878bbfac057534afdf6..b063475f4ecf5b1887f5d5b11424b253e3541abc 100644 (file)
@@ -37,6 +37,8 @@ static const struct {
        {-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
@@ -96,7 +98,7 @@ static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli,
 
        /* 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);
        
@@ -166,7 +168,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(cli->outbuf,13,0,True);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -226,7 +228,7 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
        fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
 
        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);
                        
@@ -245,9 +247,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 */
@@ -375,7 +384,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(cli->outbuf,13,0,True);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -455,11 +464,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(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);
@@ -495,10 +504,10 @@ 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));
@@ -507,7 +516,7 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
        p += clistr_pull(cli, 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));
+       len = smb_bufrem(cli->inbuf, p);
        p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), len, 0);
 
        return blob2;
@@ -579,8 +588,8 @@ 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;
                }
@@ -625,7 +634,7 @@ static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *
        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));
+               return ADS_ERROR_NT(cli_nt_error(cli));
        }
 
        cli_set_session_key(cli, session_key_krb5);
@@ -687,14 +696,14 @@ 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 == SMB_READ_BAD_SIG) {
@@ -706,7 +715,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;
@@ -752,12 +761,12 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                data_blob_free(&key);
 
                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;
                        }
                }
@@ -776,16 +785,21 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
 
 /****************************************************************************
  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));
 
@@ -802,8 +816,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);
@@ -815,62 +831,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_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);
+                       }
+
+                       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));
 }
 
 /****************************************************************************
@@ -879,8 +948,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)
@@ -888,8 +957,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;
@@ -916,8 +994,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;
                }
 
@@ -943,8 +1021,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);
@@ -953,7 +1031,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);
@@ -976,7 +1055,6 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
        }
 
        return NT_STATUS_OK;
-
 }
 
 /****************************************************************************
@@ -986,7 +1064,7 @@ NTSTATUS cli_session_setup(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);
@@ -1030,8 +1108,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;
                }
 
@@ -1043,8 +1122,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;
                        }
 
@@ -1052,7 +1132,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);
@@ -1063,7 +1143,7 @@ bool cli_send_tconX(struct cli_state *cli,
        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);
 
@@ -1095,7 +1175,7 @@ bool cli_send_tconX(struct cli_state *cli,
                /* 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 */
 
@@ -1114,15 +1194,15 @@ bool cli_send_tconX(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);
-       
+
        cli_send_smb(cli);
        if (!cli_receive_smb(cli))
                return False;
-       
+
        if (cli_is_error(cli)) {
                return False;
        }
@@ -1146,7 +1226,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;
@@ -1185,8 +1265,8 @@ bool cli_negprot(struct cli_state *cli)
             prots[numprots].name && prots[numprots].prot<=cli->protocol;
             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;
@@ -1273,9 +1353,9 @@ 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) {
@@ -1317,9 +1397,13 @@ bool cli_session_request(struct cli_state *cli,
        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);
@@ -1330,10 +1414,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
@@ -1415,11 +1495,15 @@ NTSTATUS cli_connect(struct cli_state *cli,
 
 {
        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";
+               host = star_smbserver_name;
        }
 
        fstrcpy(cli->desthost, host);
@@ -1430,45 +1514,67 @@ NTSTATUS cli_connect(struct cli_state *cli,
                *p = 0;
        }
 
-       if (!dest_ss || is_zero_addr(dest_ss)) {
-                if (!resolve_name(cli->desthost, &cli->dest_ss, name_type)) {
+       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 (dest_ss) {
-                       *dest_ss = cli->dest_ss;
-               }
        } else {
-               cli->dest_ss = *dest_ss;
+               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 */
-               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;
+       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) {
-                       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) {
-               char addr[INET6_ADDRSTRLEN];
-               if (dest_ss) {
-                       print_sockaddr(addr, sizeof(addr), dest_ss);
-               }
-               DEBUG(1,("Error connecting to %s (%s)\n",
-                        dest_ss?addr:host,strerror(errno)));
+               TALLOC_FREE(frame);
                return map_nt_error_from_unix(errno);
        }
 
+       if (dest_ss) {
+               *dest_ss = cli->dest_ss;
+       }
+
        set_socket_options(cli->fd, lp_socket_options());
 
+       TALLOC_FREE(frame);
        return NT_STATUS_OK;
 }
 
@@ -1499,11 +1605,11 @@ NTSTATUS cli_start_connection(struct cli_state **output_cli,
 
        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);
 
@@ -1517,7 +1623,7 @@ NTSTATUS cli_start_connection(struct cli_state **output_cli,
        if (dest_ss) {
                ss = *dest_ss;
        } else {
-               zero_addr(&ss);
+               zero_sockaddr(&ss);
        }
 
 again:
@@ -1545,8 +1651,8 @@ again:
                        *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;
@@ -1559,6 +1665,11 @@ again:
        else if (flags & CLI_FULL_CONNECTION_USE_KERBEROS)
                cli->use_kerberos = True;
 
+       if ((flags & CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS) &&
+            cli->use_kerberos) {
+               cli->fallback_after_kerberos = true;
+       }
+
        if (!cli_negprot(cli)) {
                DEBUG(1,("failed negprot\n"));
                nt_status = cli_nt_error(cli);
@@ -1671,7 +1782,7 @@ 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_name, 0x20);
        } else {
                make_nmb_name(&called, desthost, 0x20);
        }
@@ -1680,7 +1791,7 @@ bool attempt_netbios_session_request(struct cli_state **ppcli, const char *srcho
                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
@@ -1729,15 +1840,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);
 
@@ -1767,14 +1878,22 @@ NTSTATUS cli_raw_tcon(struct cli_state *cli,
 
 struct cli_state *get_ipc_connect(char *server,
                                struct sockaddr_storage *server_ss,
-                               struct user_auth_info *user_info)
+                               const struct user_auth_info *user_info)
 {
         struct cli_state *cli;
        NTSTATUS nt_status;
+       uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
+
+       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, lp_workgroup(), user_info->password, 
-                                       CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK, Undefined, NULL);
+                                       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;
@@ -1803,13 +1922,18 @@ struct cli_state *get_ipc_connect(char *server,
  * 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)
 {
        char addr[INET6_ADDRSTRLEN];
         fstring name;
        struct cli_state *cli;
        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",
                    addr));
@@ -1838,7 +1962,7 @@ struct cli_state *get_ipc_connect_master_ip(struct ip_service *mb_ip, pstring wo
                 return NULL;
         }
 
-       pstrcpy(workgroup, name);
+       *pp_workgroup_out = talloc_strdup(ctx, name);
 
        DEBUG(4, ("found master browser %s, %s\n", name, addr));
 
@@ -1853,12 +1977,16 @@ struct cli_state *get_ipc_connect_master_ip(struct ip_service *mb_ip, pstring wo
  * 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 */
@@ -1874,7 +2002,8 @@ struct cli_state *get_ipc_connect_master_ip_bcast(pstring workgroup, struct user
                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);
+               cli = get_ipc_connect_master_ip(ctx, &ip_list[i],
+                               user_info, pp_workgroup_out);
                if (cli)
                        return(cli);
        }