From a3e4041e2419469546bebde1ab2cb202c15be16e Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Fri, 16 Jan 2015 16:21:22 +0100 Subject: [PATCH] libsmb: reuse connections derived from DFS referrals [MS-DFSC] 3.2.1.1 and 3.2.1.2 states that DFS targets with the same site location or relative cost are placed in random order in a DFS referral response. libsmbclient currently resolves DFS referrals on every API call, always using the first entry in the referral response. With random ordering, libsmbclient may open a new server connection, rather than reuse an existing (cached) connection established in a previous DFS referred API call. This change sees libsmbclient check the connection cache for any of the DFS referral response entries before creating a new connection. This change is based on a patch by Har Gagan Sahai . Bug: https://bugzilla.samba.org/show_bug.cgi?id=10123 Signed-off-by: David Disseldorp Reviewed-by: Jeremy Allison (cherry picked from commit 7b7d4f740fe5017107d3100041cc8c7982f0eac7) [ddiss@samba.org: 3.6 rebase without smbXcli_tcon or ntstatus return] --- source3/libsmb/clidfs.c | 97 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c index 23e1471..1d4b6be 100644 --- a/source3/libsmb/clidfs.c +++ b/source3/libsmb/clidfs.c @@ -767,6 +767,11 @@ NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx, /******************************************************************** ********************************************************************/ +struct cli_dfs_path_split { + char *server; + char *share; + char *extrapath; +}; bool cli_resolve_path(TALLOC_CTX *ctx, const char *mountpt, @@ -784,15 +789,16 @@ bool 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; SMB_STRUCT_STAT sbuf; uint32 attributes; NTSTATUS status; + struct cli_dfs_path_split *dfs_refs = NULL; if ( !rootcli || !path || !targetcli ) { return false; @@ -874,13 +880,67 @@ bool cli_resolve_path(TALLOC_CTX *ctx, return false; } - /* Just store the first referral for now. */ - if (!refs[0].dfspath) { return false; } - if (!split_dfs_path(ctx, refs[0].dfspath, &server, &share, - &extrapath)) { + + /* + * Bug#10123 - DFS referal entries can be provided in a random order, + * so check the connection cache for each item to avoid unnecessary + * reconnections. + */ + dfs_refs = talloc_array(ctx, struct cli_dfs_path_split, num_refs); + if (dfs_refs == NULL) { + return false; + } + + for (count = 0; count < num_refs; count++) { + if (!split_dfs_path(dfs_refs, refs[count].dfspath, + &dfs_refs[count].server, + &dfs_refs[count].share, + &dfs_refs[count].extrapath)) { + TALLOC_FREE(dfs_refs); + return false; + } + + ccli = cli_cm_find(rootcli, dfs_refs[count].server, + dfs_refs[count].share); + if (ccli != NULL) { + extrapath = dfs_refs[count].extrapath; + *targetcli = ccli; + break; + } + } + + /* + * If no cached connection was found, then connect to the first live + * referral server in the list. + */ + for (count = 0; (ccli == NULL) && (count < num_refs); count++) { + /* Connect to the target server & share */ + *targetcli = cli_cm_connect(ctx, rootcli, + dfs_refs[count].server, + dfs_refs[count].share, + dfs_auth_info, + false, + (rootcli->trans_enc_state != NULL), + rootcli->protocol, + 0, + 0x20); + if (*targetcli == NULL) { + 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) { + TALLOC_FREE(dfs_refs); return false; } @@ -888,12 +948,14 @@ bool cli_resolve_path(TALLOC_CTX *ctx, dfs_path = cli_dfs_make_full_path(ctx, rootcli, path); if (!dfs_path) { + TALLOC_FREE(dfs_refs); return false; } pathlen = strlen(dfs_path); consumed = MIN(pathlen, consumed); *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed]); if (!*pp_targetpath) { + TALLOC_FREE(dfs_refs); return false; } dfs_path[consumed] = '\0'; @@ -904,21 +966,6 @@ bool cli_resolve_path(TALLOC_CTX *ctx, * (in \server\share\path format). */ - /* Open the connection to the target server & share */ - if ((*targetcli = cli_cm_open(ctx, rootcli, - server, - share, - dfs_auth_info, - false, - (rootcli->trans_enc_state != NULL), - rootcli->protocol, - 0, - 0x20)) == NULL) { - d_printf("Unable to follow dfs referral [\\%s\\%s]\n", - server, share ); - return false; - } - if (extrapath && strlen(extrapath) > 0) { /* EMC Celerra NAS version 5.6.50 (at least) doesn't appear to */ /* put the trailing \ on the path, so to be save we put one in if needed */ @@ -934,6 +981,7 @@ bool cli_resolve_path(TALLOC_CTX *ctx, *pp_targetpath); } if (!*pp_targetpath) { + TALLOC_FREE(dfs_refs); return false; } } @@ -947,18 +995,21 @@ bool cli_resolve_path(TALLOC_CTX *ctx, d_printf("cli_resolve_path: " "dfs_path (%s) not in correct format.\n", dfs_path ); + TALLOC_FREE(dfs_refs); return false; } ppath++; /* Now pointing at start of server name. */ if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) { + TALLOC_FREE(dfs_refs); return false; } ppath++; /* Now pointing at start of share name. */ if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) { + TALLOC_FREE(dfs_refs); return false; } @@ -966,6 +1017,7 @@ bool cli_resolve_path(TALLOC_CTX *ctx, newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath ); if (!newmount) { + TALLOC_FREE(dfs_refs); return false; } @@ -989,6 +1041,7 @@ bool cli_resolve_path(TALLOC_CTX *ctx, */ *targetcli = newcli; *pp_targetpath = newpath; + TALLOC_FREE(dfs_refs); return true; } } @@ -999,11 +1052,13 @@ bool cli_resolve_path(TALLOC_CTX *ctx, if ((*targetcli)->dfsroot) { dfs_path = talloc_strdup(ctx, *pp_targetpath); if (!dfs_path) { + TALLOC_FREE(dfs_refs); return false; } *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path); } + TALLOC_FREE(dfs_refs); return true; } -- 2.1.2