diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c index 80fba23..e2560d3 100644 --- a/source3/libsmb/clidfs.c +++ b/source3/libsmb/clidfs.c @@ -835,6 +835,11 @@ NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx, /******************************************************************** ********************************************************************/ +struct cli_dfs_path_split { + char *server; + char *share; + char *extrapath; +}; NTSTATUS cli_resolve_path(TALLOC_CTX *ctx, const char *mountpt, @@ -852,9 +857,9 @@ NTSTATUS cli_resolve_path(TALLOC_CTX *ctx, char *cleanpath = NULL; char *extrapath = NULL; int pathlen; - char *server = NULL; - char *share = NULL; struct cli_state *newcli = NULL; + struct cli_state *ccli = NULL; + int count = 0; char *newpath = NULL; char *newmount = NULL; char *ppath = NULL; @@ -863,6 +868,7 @@ NTSTATUS cli_resolve_path(TALLOC_CTX *ctx, NTSTATUS status; struct smbXcli_tcon *root_tcon = NULL; struct smbXcli_tcon *target_tcon = NULL; + struct cli_dfs_path_split *dfs_refs = NULL; if ( !rootcli || !path || !targetcli ) { return NT_STATUS_INVALID_PARAMETER; @@ -957,9 +963,44 @@ NTSTATUS cli_resolve_path(TALLOC_CTX *ctx, if (!refs[0].dfspath) { return NT_STATUS_NOT_FOUND; } - if (!split_dfs_path(ctx, refs[0].dfspath, &server, &share, - &extrapath)) { - return NT_STATUS_NOT_FOUND; + + /* + * Bug#10123 - Go through the cached connection for the given context. + * This is to avoid reconnection issues to another server when it is + * a DFS based share. It checks if the referral is still alive by sending + * keep alive request. + */ + + dfs_refs = talloc_array(ctx, struct cli_dfs_path_split, num_refs); + if (dfs_refs == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* Finding the cached cli connect. Use it only if it is alive. */ + for (count = 0; count < num_refs ; count++) { + if (!split_dfs_path(ctx, refs[count].dfspath, + &dfs_refs[count].server, + &dfs_refs[count].share, + &dfs_refs[count].extrapath)) { + return NT_STATUS_NOT_FOUND; + } + + ccli = cli_cm_find(rootcli, dfs_refs[count].server, + dfs_refs[count].share); + if (ccli != NULL) { + NTSTATUS status; + unsigned char keepalive[16]; + memset(keepalive, 0xf0, sizeof(keepalive)); + status = cli_echo(ccli, 1, + data_blob_const(keepalive, sizeof(keepalive))); + if (NT_STATUS_IS_OK(status)) { + extrapath = dfs_refs[count].extrapath; + break; + } else { + ccli = NULL; + continue; + } + } } /* Make sure to recreate the original string including any wildcards. */ @@ -982,21 +1023,41 @@ NTSTATUS cli_resolve_path(TALLOC_CTX *ctx, * (in \server\share\path format). */ - /* Open the connection to the target server & share */ - status = cli_cm_open(ctx, rootcli, - server, - share, - dfs_auth_info, - false, - smb1cli_conn_encryption_on(rootcli->conn), - smbXcli_conn_protocol(rootcli->conn), - 0, - 0x20, - targetcli); - if (!NT_STATUS_IS_OK(status)) { - d_printf("Unable to follow dfs referral [\\%s\\%s]\n", - server, share ); - return status; + /* + * Reuse the previous connection if it still active. Otherwise connect to first + * live referral server from the list. + */ + if (ccli != NULL) { + *targetcli = ccli; + } else { + for (count = 0; count < num_refs ; count++) { + /* Connect to the target server & share */ + status = cli_cm_connect(ctx, rootcli, + dfs_refs[count].server, + dfs_refs[count].share, + dfs_auth_info, + false, + smb1cli_conn_encryption_on(rootcli->conn), + smbXcli_conn_protocol(rootcli->conn), + 0, + 0x20, + targetcli); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Unable to follow dfs referral [\\%s\\%s]\n", + dfs_refs[count].server + dfs_refs[count].share); + continue; + } + else { + extrapath = dfs_refs[count].extrapath; + break; + } + } + + /* No available referral server for the connection */ + if (*targetcli == NULL) { + return status; + } } if (extrapath && strlen(extrapath) > 0) {