diff options
Diffstat (limited to 'source3/libsmb/clidfs.c')
-rw-r--r-- | source3/libsmb/clidfs.c | 257 |
1 files changed, 229 insertions, 28 deletions
diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c index bb82fbfabb..ea66eba998 100644 --- a/source3/libsmb/clidfs.c +++ b/source3/libsmb/clidfs.c @@ -1,8 +1,8 @@ /* Unix SMB/CIFS implementation. client connect/disconnect routines - Copyright (C) Gerald (Jerry) Carter - + Copyright (C) Gerald (Jerry) Carter 2004 + 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 @@ -22,30 +22,12 @@ #include "includes.h" -/******************************************************************** - check for dfs referral -********************************************************************/ - -BOOL check_for_dfs_referral( struct cli_state *cli ) -{ - uint32 flgs2 = SVAL(cli->inbuf,smb_flg2); - - /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */ - - if ( !( (flgs2&FLAGS2_32_BIT_ERROR_CODES) && (flgs2&FLAGS2_UNICODE_STRINGS) ) ) - return False; - - if ( NT_STATUS_EQUAL( NT_STATUS_PATH_NOT_COVERED, NT_STATUS(IVAL(cli->inbuf,smb_rcls)) ) ) - return True; - - return False; -} /******************************************************************** split a dfs path into the server and share name components ********************************************************************/ -void split_dfs_path( const char *nodepath, fstring server, fstring share ) +static void split_dfs_path( const char *nodepath, fstring server, fstring share ) { char *p; pstring path; @@ -67,12 +49,112 @@ void split_dfs_path( const char *nodepath, fstring server, fstring share ) fstrcpy( server, &path[1] ); } +/**************************************************************************** + return the original path truncated at the first wildcard character + (also strips trailing \'s). Trust the caller to provide a NULL + terminated string +****************************************************************************/ + +static void clean_path( pstring clean, const char *path ) +{ + int len; + char *p; + pstring newpath; + + pstrcpy( newpath, path ); + p = newpath; + + while ( p ) { + /* first check for '*' */ + + p = strrchr_m( newpath, '*' ); + if ( p ) { + *p = '\0'; + p = newpath; + continue; + } + + /* first check for '?' */ + + p = strrchr_m( newpath, '?' ); + if ( p ) { + *p = '\0'; + p = newpath; + } + } + + /* strip a trailing backslash */ + + len = strlen( newpath ); + if ( newpath[len-1] == '\\' ) + newpath[len-1] = '\0'; + + pstrcpy( clean, newpath ); +} + +/**************************************************************************** +****************************************************************************/ + +static BOOL make_full_path( pstring path, const char *server, const char *share, + const char *dir ) +{ + pstring servicename; + char *sharename; + const char *directory; + + + /* make a copy so we don't modify the global string 'service' */ + + pstrcpy(servicename, share); + sharename = servicename; + + if (*sharename == '\\') { + + server = sharename+2; + sharename = strchr_m(server,'\\'); + + if (!sharename) + return False; + + *sharename = 0; + sharename++; + } + + directory = dir; + if ( *directory == '\\' ) + directory++; + + pstr_sprintf( path, "\\%s\\%s\\%s", server, sharename, directory ); + + return True; +} + +/******************************************************************** + check for dfs referral +********************************************************************/ + +static BOOL cli_dfs_check_error( struct cli_state *cli ) +{ + uint32 flgs2 = SVAL(cli->inbuf,smb_flg2); + + /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */ + + if ( !( (flgs2&FLAGS2_32_BIT_ERROR_CODES) && (flgs2&FLAGS2_UNICODE_STRINGS) ) ) + return False; + + if ( NT_STATUS_EQUAL( NT_STATUS_PATH_NOT_COVERED, NT_STATUS(IVAL(cli->inbuf,smb_rcls)) ) ) + return True; + + return False; +} + /******************************************************************** get the dfs referral link ********************************************************************/ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, - struct referral **refs, size_t *num_refs) + CLIENT_DFS_REFERRAL**refs, size_t *num_refs, + uint16 *consumed) { unsigned int data_len = 0; unsigned int param_len = 0; @@ -83,7 +165,7 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, char *p; size_t pathlen = 2*(strlen(path)+1); uint16 num_referrals; - struct referral *referrals; + CLIENT_DFS_REFERRAL *referrals; memset(param, 0, sizeof(param)); SSVAL(param, 0, 0x03); /* max referral level */ @@ -108,6 +190,7 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, return False; } + *consumed = SVAL( rdata, 0 ); num_referrals = SVAL( rdata, 2 ); if ( num_referrals != 0 ) { @@ -117,7 +200,7 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, uint16 node_offset; - referrals = SMB_XMALLOC_ARRAY( struct referral, num_referrals ); + referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL, num_referrals ); /* start at the referrals array */ @@ -132,11 +215,11 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, continue; } - referrals[0].proximity = SVAL( p, 8 ); - referrals[0].ttl = SVAL( p, 10 ); + referrals[i].proximity = SVAL( p, 8 ); + referrals[i].ttl = SVAL( p, 10 ); - clistr_pull( cli, referrals[0].alternate_path, p+node_offset, - sizeof(referrals[0].alternate_path), -1, STR_TERMINATE|STR_UNICODE ); + clistr_pull( cli, referrals[i].dfspath, p+node_offset, + sizeof(referrals[i].dfspath), -1, STR_TERMINATE|STR_UNICODE ); p += ref_size; } @@ -152,3 +235,121 @@ BOOL cli_dfs_get_referral( struct cli_state *cli, const char *path, return True; } +#if 0 +/******************************************************************** +********************************************************************/ + +BOOL cli_dfs_handle_referral( struct cli_state *cli, const char *path ) +{ + struct cli_state *cli_ipc; + pstring fullpath; + CLIENT_DFS_REFERRAL *refs = NULL; + size_t num_refs; + uint16 consumed; + fstring server, share; + + if ( !cli_dfs_check_error(cli) ) + return False; + + if ( !(cli_ipc = cli_cm_open( cli->desthost, "IPC$", False )) ) + return False; + + make_full_path( fullpath, cli->desthost, cli->share, path ); + + if ( !cli_dfs_get_referral( cli_ipc, fullpath, &refs, &num_refs, &consumed ) ) { + d_printf("cli_get_dfs_referral() failed!\n"); + /* reset the current client connection */ + cli_cm_open( cli->desthost, cli->share, False ); + + return False; + } + + /* just pick the first one */ + if ( num_refs ) { + split_dfs_path( refs[0].alternate_path, server, share ); + if ( cli_cm_open( server, share, False ) == NULL ) { + d_printf("Unable to follow dfs referral [\\\\%s\\%s]\n", + server, share ); + cli_cm_open( cli->desthost, cli->share, False ); + + return False; + } + } + + SAFE_FREE( refs ); + + return True; +} +#endif + +/******************************************************************** +********************************************************************/ + +BOOL cli_resolve_path( struct cli_state *rootcli, const char *path, + struct cli_state **targetcli, pstring targetpath ) +{ + CLIENT_DFS_REFERRAL *refs = NULL; + size_t num_refs; + uint16 consumed; + struct cli_state *cli_ipc; + pstring fullpath, cleanpath; + int pathlen; + fstring server, share; + + SMB_STRUCT_STAT sbuf; + uint32 attributes; + + if ( !rootcli || !path || !targetcli ) + return False; + + /* send a trans2_query_path_info to check for a referral */ + + clean_path( cleanpath, path ); + make_full_path( fullpath, rootcli->desthost, rootcli->share, cleanpath ); + + /* don't bother continuing if this is not a dfs root */ + + if ( !rootcli->dfsroot || cli_qpathinfo_basic( rootcli, fullpath, &sbuf, &attributes ) ) { + *targetcli = rootcli; + pstrcpy( targetpath, path ); + return True; + } + + /* we got an error, check for DFS referral */ + + if ( !cli_dfs_check_error(rootcli) ) + return False; + + /* check for the referral */ + + if ( !(cli_ipc = cli_cm_open( rootcli->desthost, "IPC$", False )) ) + return False; + + if ( !cli_dfs_get_referral(cli_ipc, fullpath, &refs, &num_refs, &consumed) + || !num_refs ) + { + return False; + } + + /* just store the first referral for now + Make sure to recreate the original string including any wildcards */ + + make_full_path( fullpath, rootcli->desthost, rootcli->share, path ); + pathlen = strlen( fullpath )*2; + consumed = MIN(pathlen, consumed ); + pstrcpy( targetpath, &fullpath[consumed/2] ); + + split_dfs_path( refs[0].dfspath, server, share ); + SAFE_FREE( refs ); + + /* open the connection to the target path */ + + if ( (*targetcli = cli_cm_open(server, share, False)) == NULL ) { + d_printf("Unable to follow dfs referral [//%s/%s]\n", + server, share ); + + return False; + } + + return True; +} |