Fix clidfs.c compile.
[ira/wip.git] / source3 / libsmb / clidfs.c
1 /*
2    Unix SMB/CIFS implementation.
3    client connect/disconnect routines
4    Copyright (C) Andrew Tridgell                  1994-1998
5    Copyright (C) Gerald (Jerry) Carter            2004
6    Copyright (C) Jeremy Allison                   2007
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23
24 /********************************************************************
25  Important point.
26
27  DFS paths are *always* of the form \server\share\<pathname> (the \ characters
28  are not C escaped here).
29
30  - but if we're using POSIX paths then <pathname> may contain
31    '/' separators, not '\\' separators. So cope with '\\' or '/'
32    as a separator when looking at the pathname part.... JRA.
33 ********************************************************************/
34
35 struct client_connection {
36         struct client_connection *prev, *next;
37         struct cli_state *cli;
38         char *mount;
39 };
40
41 /* global state....globals reek! */
42 int max_protocol = PROTOCOL_NT1;
43
44 static struct cm_cred_struct {
45         char *username;
46         char *password;
47         bool got_pass;
48         bool use_kerberos;
49         int signing_state;
50 } cm_creds;
51
52 static void cm_set_password(const char *newpass);
53
54 static int port;
55 static int name_type = 0x20;
56 static bool have_ip;
57 static struct sockaddr_storage dest_ss;
58
59 static struct client_connection *connections;
60
61 /********************************************************************
62  Return a connection to a server.
63 ********************************************************************/
64
65 static struct cli_state *do_connect(TALLOC_CTX *ctx,
66                                         const char *server,
67                                         const char *share,
68                                         bool show_sessetup)
69 {
70         struct cli_state *c = NULL;
71         struct nmb_name called, calling;
72         const char *server_n;
73         struct sockaddr_storage ss;
74         char *servicename;
75         char *sharename;
76         char *newserver, *newshare;
77         const char *username;
78         const char *password;
79         NTSTATUS status;
80
81         /* make a copy so we don't modify the global string 'service' */
82         servicename = talloc_strdup(ctx,share);
83         if (!servicename) {
84                 return NULL;
85         }
86         sharename = servicename;
87         if (*sharename == '\\') {
88                 server = sharename+2;
89                 sharename = strchr_m(server,'\\');
90                 if (!sharename) {
91                         return NULL;
92                 }
93                 *sharename = 0;
94                 sharename++;
95         }
96
97         server_n = server;
98
99         zero_addr(&ss);
100
101         make_nmb_name(&calling, global_myname(), 0x0);
102         make_nmb_name(&called , server, name_type);
103
104  again:
105         zero_addr(&ss);
106         if (have_ip)
107                 ss = dest_ss;
108
109         /* have to open a new connection */
110         if (!(c=cli_initialise()) || (cli_set_port(c, port) != port)) {
111                 d_printf("Connection to %s failed\n", server_n);
112                 return NULL;
113         }
114         status = cli_connect(c, server_n, &ss);
115         if (!NT_STATUS_IS_OK(status)) {
116                 d_printf("Connection to %s failed (Error %s)\n",
117                                 server_n,
118                                 nt_errstr(status));
119                 return NULL;
120         }
121
122         c->protocol = max_protocol;
123         c->use_kerberos = cm_creds.use_kerberos;
124         cli_setup_signing_state(c, cm_creds.signing_state);
125
126         if (!cli_session_request(c, &calling, &called)) {
127                 char *p;
128                 d_printf("session request to %s failed (%s)\n",
129                          called.name, cli_errstr(c));
130                 cli_shutdown(c);
131                 c = NULL;
132                 if ((p=strchr_m(called.name, '.'))) {
133                         *p = 0;
134                         goto again;
135                 }
136                 if (strcmp(called.name, "*SMBSERVER")) {
137                         make_nmb_name(&called , "*SMBSERVER", 0x20);
138                         goto again;
139                 }
140                 return NULL;
141         }
142
143         DEBUG(4,(" session request ok\n"));
144
145         if (!cli_negprot(c)) {
146                 d_printf("protocol negotiation failed\n");
147                 cli_shutdown(c);
148                 return NULL;
149         }
150
151         if (!cm_creds.got_pass) {
152                 char *pass = getpass("Password: ");
153                 if (pass) {
154                         cm_set_password(pass);
155                 }
156         }
157
158         username = cm_creds.username ? cm_creds.username : "";
159         password = cm_creds.password ? cm_creds.password : "";
160
161         if (!NT_STATUS_IS_OK(cli_session_setup(c, username,
162                                                password, strlen(password),
163                                                password, strlen(password),
164                                                lp_workgroup()))) {
165                 /* If a password was not supplied then
166                  * try again with a null username. */
167                 if (password[0] || !username[0] || cm_creds.use_kerberos ||
168                     !NT_STATUS_IS_OK(cli_session_setup(c, "",
169                                                 "", 0,
170                                                 "", 0,
171                                                lp_workgroup()))) {
172                         d_printf("session setup failed: %s\n", cli_errstr(c));
173                         if (NT_STATUS_V(cli_nt_error(c)) ==
174                             NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED))
175                                 d_printf("did you forget to run kinit?\n");
176                         cli_shutdown(c);
177                         return NULL;
178                 }
179                 d_printf("Anonymous login successful\n");
180         }
181
182         if ( show_sessetup ) {
183                 if (*c->server_domain) {
184                         DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n",
185                                 c->server_domain,c->server_os,c->server_type));
186                 } else if (*c->server_os || *c->server_type) {
187                         DEBUG(0,("OS=[%s] Server=[%s]\n",
188                                  c->server_os,c->server_type));
189                 }
190         }
191         DEBUG(4,(" session setup ok\n"));
192
193         /* here's the fun part....to support 'msdfs proxy' shares
194            (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
195            here before trying to connect to the original share.
196            check_dfs_proxy() will fail if it is a normal share. */
197
198         if ((c->capabilities & CAP_DFS) &&
199                         cli_check_msdfs_proxy(ctx, c, sharename,
200                                 &newserver, &newshare)) {
201                 cli_shutdown(c);
202                 return do_connect(ctx, newserver, newshare, false);
203         }
204
205         /* must be a normal share */
206
207         if (!cli_send_tconX(c, sharename, "?????",
208                                 password, strlen(password)+1)) {
209                 d_printf("tree connect failed: %s\n", cli_errstr(c));
210                 cli_shutdown(c);
211                 return NULL;
212         }
213
214         DEBUG(4,(" tconx ok\n"));
215         return c;
216 }
217
218 /****************************************************************************
219 ****************************************************************************/
220
221 static void cli_cm_set_mntpoint(struct cli_state *c, const char *mnt)
222 {
223         struct client_connection *p;
224         int i;
225
226         for (p=connections,i=0; p; p=p->next,i++) {
227                 if (strequal(p->cli->desthost, c->desthost) &&
228                                 strequal(p->cli->share, c->share)) {
229                         break;
230                 }
231         }
232
233         if (p) {
234                 char *name = clean_name(NULL, p->mount);
235                 if (!name) {
236                         return;
237                 }
238                 p->mount = talloc_strdup(p, name);
239                 TALLOC_FREE(name);
240         }
241 }
242
243 /****************************************************************************
244 ****************************************************************************/
245
246 const char *cli_cm_get_mntpoint(struct cli_state *c)
247 {
248         struct client_connection *p;
249         int i;
250
251         for (p=connections,i=0; p; p=p->next,i++) {
252                 if (strequal(p->cli->desthost, c->desthost) &&
253                                 strequal(p->cli->share, c->share)) {
254                         break;
255                 }
256         }
257
258         if (p) {
259                 return p->mount;
260         }
261         return NULL;
262 }
263
264 /********************************************************************
265  Add a new connection to the list
266 ********************************************************************/
267
268 static struct cli_state *cli_cm_connect(TALLOC_CTX *ctx,
269                                         struct cli_state *referring_cli,
270                                         const char *server,
271                                         const char *share,
272                                         bool show_hdr)
273 {
274         struct client_connection *node;
275
276         /* NB This must be the null context here... JRA. */
277         node = TALLOC_ZERO_ARRAY(NULL, struct client_connection, 1);
278         if (!node) {
279                 return NULL;
280         }
281
282         node->cli = do_connect(ctx, server, share, show_hdr);
283
284         if ( !node->cli ) {
285                 TALLOC_FREE( node );
286                 return NULL;
287         }
288
289         DLIST_ADD( connections, node );
290
291         cli_cm_set_mntpoint(node->cli, "");
292
293         if (referring_cli && referring_cli->posix_capabilities) {
294                 uint16 major, minor;
295                 uint32 caplow, caphigh;
296                 if (cli_unix_extensions_version(node->cli, &major,
297                                         &minor, &caplow, &caphigh)) {
298                         cli_set_unix_extensions_capabilities(node->cli,
299                                         major, minor,
300                                         caplow, caphigh);
301                 }
302         }
303
304         return node->cli;
305 }
306
307 /********************************************************************
308  Return a connection to a server.
309 ********************************************************************/
310
311 static struct cli_state *cli_cm_find(const char *server, const char *share)
312 {
313         struct client_connection *p;
314
315         for (p=connections; p; p=p->next) {
316                 if ( strequal(server, p->cli->desthost) &&
317                                 strequal(share,p->cli->share)) {
318                         return p->cli;
319                 }
320         }
321
322         return NULL;
323 }
324
325 /****************************************************************************
326  Open a client connection to a \\server\share.  Set's the current *cli
327  global variable as a side-effect (but only if the connection is successful).
328 ****************************************************************************/
329
330 struct cli_state *cli_cm_open(TALLOC_CTX *ctx,
331                                 struct cli_state *referring_cli,
332                                 const char *server,
333                                 const char *share,
334                                 bool show_hdr)
335 {
336         struct cli_state *c;
337
338         /* try to reuse an existing connection */
339
340         c = cli_cm_find(server, share);
341         if (!c) {
342                 c = cli_cm_connect(ctx, referring_cli, server, share, show_hdr);
343         }
344
345         return c;
346 }
347
348 /****************************************************************************
349 ****************************************************************************/
350
351 void cli_cm_shutdown(void)
352 {
353         struct client_connection *p, *x;
354
355         for (p=connections; p;) {
356                 cli_shutdown(p->cli);
357                 x = p;
358                 p = p->next;
359
360                 TALLOC_FREE(x);
361         }
362
363         connections = NULL;
364         return;
365 }
366
367 /****************************************************************************
368 ****************************************************************************/
369
370 void cli_cm_display(void)
371 {
372         struct client_connection *p;
373         int i;
374
375         for ( p=connections,i=0; p; p=p->next,i++ ) {
376                 d_printf("%d:\tserver=%s, share=%s\n",
377                         i, p->cli->desthost, p->cli->share );
378         }
379 }
380
381 /****************************************************************************
382 ****************************************************************************/
383
384 static void cm_set_password(const char *newpass)
385 {
386         SAFE_FREE(cm_creds.password);
387         cm_creds.password = SMB_STRDUP(newpass);
388         if (cm_creds.password) {
389                 cm_creds.got_pass = true;
390         }
391 }
392
393 void cli_cm_set_credentials(void)
394 {
395         SAFE_FREE(cm_creds.username);
396         cm_creds.username = SMB_STRDUP(get_cmdline_auth_info_username());
397
398         if (get_cmdline_auth_info_got_pass()) {
399                 cm_set_password(get_cmdline_auth_info_password());
400         }
401
402         cm_creds.use_kerberos = get_cmdline_auth_info_use_kerberos();
403         cm_creds.signing_state = get_cmdline_auth_info_signing_state();
404 }
405
406 /****************************************************************************
407 ****************************************************************************/
408
409 void cli_cm_set_port(int port_number)
410 {
411         port = port_number;
412 }
413
414 /****************************************************************************
415 ****************************************************************************/
416
417 void cli_cm_set_dest_name_type(int type)
418 {
419         name_type = type;
420 }
421
422 /****************************************************************************
423 ****************************************************************************/
424
425 void cli_cm_set_dest_ss(struct sockaddr_storage *pss)
426 {
427         dest_ss = *pss;
428         have_ip = true;
429 }
430
431 /**********************************************************************
432  split a dfs path into the server, share name, and extrapath components
433 **********************************************************************/
434
435 static void split_dfs_path(TALLOC_CTX *ctx,
436                                 const char *nodepath,
437                                 char **pp_server,
438                                 char **pp_share,
439                                 char **pp_extrapath)
440 {
441         char *p, *q;
442         char *path;
443
444         *pp_server = NULL;
445         *pp_share = NULL;
446         *pp_extrapath = NULL;
447
448         path = talloc_strdup(ctx, nodepath);
449         if (!path) {
450                 return;
451         }
452
453         if ( path[0] != '\\' ) {
454                 return;
455         }
456
457         p = strchr_m( path + 1, '\\' );
458         if ( !p ) {
459                 return;
460         }
461
462         *p = '\0';
463         p++;
464
465         /* Look for any extra/deep path */
466         q = strchr_m(p, '\\');
467         if (q != NULL) {
468                 *q = '\0';
469                 q++;
470                 *pp_extrapath = talloc_strdup(ctx, q);
471         } else {
472                 *pp_extrapath = talloc_strdup(ctx, "");
473         }
474
475         *pp_share = talloc_strdup(ctx, p);
476         *pp_server = talloc_strdup(ctx, &path[1]);
477 }
478
479 /****************************************************************************
480  Return the original path truncated at the directory component before
481  the first wildcard character. Trust the caller to provide a NULL
482  terminated string
483 ****************************************************************************/
484
485 static char *clean_path(TALLOC_CTX *ctx, const char *path)
486 {
487         size_t len;
488         char *p1, *p2, *p;
489         char *path_out;
490
491         /* No absolute paths. */
492         while (IS_DIRECTORY_SEP(*path)) {
493                 path++;
494         }
495
496         path_out = talloc_strdup(ctx, path);
497         if (!path_out) {
498                 return NULL;
499         }
500
501         p1 = strchr_m(path_out, '*');
502         p2 = strchr_m(path_out, '?');
503
504         if (p1 || p2) {
505                 if (p1 && p2) {
506                         p = MIN(p1,p2);
507                 } else if (!p1) {
508                         p = p2;
509                 } else {
510                         p = p1;
511                 }
512                 *p = '\0';
513
514                 /* Now go back to the start of this component. */
515                 p1 = strrchr_m(path_out, '/');
516                 p2 = strrchr_m(path_out, '\\');
517                 p = MAX(p1,p2);
518                 if (p) {
519                         *p = '\0';
520                 }
521         }
522
523         /* Strip any trailing separator */
524
525         len = strlen(path_out);
526         if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
527                 path_out[len-1] = '\0';
528         }
529
530         return path_out;
531 }
532
533 /****************************************************************************
534 ****************************************************************************/
535
536 static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
537                                         struct cli_state *cli,
538                                         const char *dir)
539 {
540         /* Ensure the extrapath doesn't start with a separator. */
541         while (IS_DIRECTORY_SEP(*dir)) {
542                 dir++;
543         }
544
545         return talloc_asprintf(ctx, "\\%s\\%s\\%s",
546                         cli->desthost, cli->share, dir);
547 }
548
549 /********************************************************************
550  check for dfs referral
551 ********************************************************************/
552
553 static bool cli_dfs_check_error( struct cli_state *cli, NTSTATUS status )
554 {
555         uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
556
557         /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
558
559         if (!((flgs2&FLAGS2_32_BIT_ERROR_CODES) &&
560                                 (flgs2&FLAGS2_UNICODE_STRINGS)))
561                 return false;
562
563         if (NT_STATUS_EQUAL(status, NT_STATUS(IVAL(cli->inbuf,smb_rcls))))
564                 return true;
565
566         return false;
567 }
568
569 /********************************************************************
570  Get the dfs referral link.
571 ********************************************************************/
572
573 bool cli_dfs_get_referral(struct cli_state *cli,
574                         const char *path,
575                         CLIENT_DFS_REFERRAL**refs,
576                         size_t *num_refs,
577                         uint16 *consumed)
578 {
579         unsigned int data_len = 0;
580         unsigned int param_len = 0;
581         uint16 setup = TRANSACT2_GET_DFS_REFERRAL;
582         char param[1024+2];
583         char *rparam=NULL, *rdata=NULL;
584         char *p;
585         size_t pathlen = 2*(strlen(path)+1);
586         uint16 num_referrals;
587         CLIENT_DFS_REFERRAL *referrals = NULL;
588
589         memset(param, 0, sizeof(param));
590         SSVAL(param, 0, 0x03);  /* max referral level */
591         p = &param[2];
592
593         p += clistr_push(cli, p, path, MIN(pathlen, sizeof(param)-2),
594                         STR_TERMINATE);
595         param_len = PTR_DIFF(p, param);
596
597         if (!cli_send_trans(cli, SMBtrans2,
598                 NULL,                        /* name */
599                 -1, 0,                          /* fid, flags */
600                 &setup, 1, 0,                   /* setup, length, max */
601                 param, param_len, 2,            /* param, length, max */
602                 NULL, 0, cli->max_xmit /* data, length, max */
603                 )) {
604                         return false;
605         }
606
607         if (!cli_receive_trans(cli, SMBtrans2,
608                 &rparam, &param_len,
609                 &rdata, &data_len)) {
610                         return false;
611         }
612
613         *consumed     = SVAL( rdata, 0 );
614         num_referrals = SVAL( rdata, 2 );
615
616         if ( num_referrals != 0 ) {
617                 uint16 ref_version;
618                 uint16 ref_size;
619                 int i;
620                 uint16 node_offset;
621
622                 referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL,
623                                 num_referrals);
624
625                 /* start at the referrals array */
626
627                 p = rdata+8;
628                 for ( i=0; i<num_referrals; i++ ) {
629                         ref_version = SVAL( p, 0 );
630                         ref_size    = SVAL( p, 2 );
631                         node_offset = SVAL( p, 16 );
632
633                         if ( ref_version != 3 ) {
634                                 p += ref_size;
635                                 continue;
636                         }
637
638                         referrals[i].proximity = SVAL( p, 8 );
639                         referrals[i].ttl       = SVAL( p, 10 );
640
641                         clistr_pull( cli, referrals[i].dfspath, p+node_offset,
642                                 sizeof(referrals[i].dfspath), -1,
643                                 STR_TERMINATE|STR_UNICODE );
644
645                         p += ref_size;
646                 }
647         }
648
649         *num_refs = num_referrals;
650         *refs = referrals;
651
652         SAFE_FREE(rdata);
653         SAFE_FREE(rparam);
654
655         return true;
656 }
657
658
659 /********************************************************************
660 ********************************************************************/
661
662 bool cli_resolve_path(TALLOC_CTX *ctx,
663                         const char *mountpt,
664                         struct cli_state *rootcli,
665                         const char *path,
666                         struct cli_state **targetcli,
667                         char **pp_targetpath)
668 {
669         CLIENT_DFS_REFERRAL *refs = NULL;
670         size_t num_refs;
671         uint16 consumed;
672         struct cli_state *cli_ipc = NULL;
673         char *dfs_path = NULL;
674         char *cleanpath = NULL;
675         char *extrapath = NULL;
676         int pathlen;
677         char *server = NULL;
678         char *share = NULL;
679         struct cli_state *newcli = NULL;
680         char *newpath = NULL;
681         char *newmount = NULL;
682         char *ppath = NULL;
683         SMB_STRUCT_STAT sbuf;
684         uint32 attributes;
685
686         if ( !rootcli || !path || !targetcli ) {
687                 return false;
688         }
689
690         /* Don't do anything if this is not a DFS root. */
691
692         if ( !rootcli->dfsroot) {
693                 *targetcli = rootcli;
694                 *pp_targetpath = talloc_strdup(ctx, path);
695                 if (!*pp_targetpath) {
696                         return false;
697                 }
698                 return true;
699         }
700
701         *targetcli = NULL;
702
703         /* Send a trans2_query_path_info to check for a referral. */
704
705         cleanpath = clean_path(ctx, path);
706         if (!cleanpath) {
707                 return false;
708         }
709
710         dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
711         if (!dfs_path) {
712                 return false;
713         }
714
715         if (cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes)) {
716                 /* This is an ordinary path, just return it. */
717                 *targetcli = rootcli;
718                 *pp_targetpath = talloc_strdup(ctx, path);
719                 if (!*pp_targetpath) {
720                         return false;
721                 }
722                 goto done;
723         }
724
725         /* Special case where client asked for a path that does not exist */
726
727         if (cli_dfs_check_error(rootcli, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
728                 *targetcli = rootcli;
729                 *pp_targetpath = talloc_strdup(ctx, path);
730                 if (!*pp_targetpath) {
731                         return false;
732                 }
733                 goto done;
734         }
735
736         /* We got an error, check for DFS referral. */
737
738         if (!cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED)) {
739                 return false;
740         }
741
742         /* Check for the referral. */
743
744         if (!(cli_ipc = cli_cm_open(ctx, rootcli,
745                                         rootcli->desthost, "IPC$", false))) {
746                 return false;
747         }
748
749         if (!cli_dfs_get_referral(cli_ipc, dfs_path, &refs,
750                         &num_refs, &consumed) || !num_refs) {
751                 return false;
752         }
753
754         /* Just store the first referral for now. */
755
756         split_dfs_path(ctx, refs[0].dfspath, &server, &share, &extrapath );
757         SAFE_FREE(refs);
758
759         if (!server || !share) {
760                 return false;
761         }
762
763         /* Make sure to recreate the original string including any wildcards. */
764
765         dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
766         if (!dfs_path) {
767                 return false;
768         }
769         pathlen = strlen(dfs_path)*2;
770         consumed = MIN(pathlen, consumed);
771         *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed/2]);
772         if (!*pp_targetpath) {
773                 return false;
774         }
775         dfs_path[consumed/2] = '\0';
776
777         /*
778          * *pp_targetpath is now the unconsumed part of the path.
779          * dfs_path is now the consumed part of the path
780          * (in \server\share\path format).
781          */
782
783         /* Open the connection to the target server & share */
784         if ((*targetcli = cli_cm_open(ctx, rootcli,
785                                         server, share, false)) == NULL) {
786                 d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
787                         server, share );
788                 return false;
789         }
790
791         if (extrapath && strlen(extrapath) > 0) {
792                 *pp_targetpath = talloc_asprintf(ctx,
793                                                 "%s%s",
794                                                 extrapath,
795                                                 *pp_targetpath);
796                 if (!*pp_targetpath) {
797                         return false;
798                 }
799         }
800
801         /* parse out the consumed mount path */
802         /* trim off the \server\share\ */
803
804         ppath = dfs_path;
805
806         if (*ppath != '\\') {
807                 d_printf("cli_resolve_path: "
808                         "dfs_path (%s) not in correct format.\n",
809                         dfs_path );
810                 return false;
811         }
812
813         ppath++; /* Now pointing at start of server name. */
814
815         if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
816                 return false;
817         }
818
819         ppath++; /* Now pointing at start of share name. */
820
821         if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
822                 return false;
823         }
824
825         ppath++; /* Now pointing at path component. */
826
827         newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
828         if (!newmount) {
829                 return false;
830         }
831
832         cli_cm_set_mntpoint(*targetcli, newmount);
833
834         /* Check for another dfs referral, note that we are not
835            checking for loops here. */
836
837         if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
838                 if (cli_resolve_path(ctx,
839                                         newmount,
840                                         *targetcli,
841                                         *pp_targetpath,
842                                         &newcli,
843                                         &newpath)) {
844                         /*
845                          * When cli_resolve_path returns true here it's always
846                          * returning the complete path in newpath, so we're done
847                          * here.
848                          */
849                         *targetcli = newcli;
850                         *pp_targetpath = newpath;
851                         return true;
852                 }
853         }
854
855   done:
856
857         /* If returning true ensure we return a dfs root full path. */
858         if ((*targetcli)->dfsroot) {
859                 dfs_path = talloc_strdup(ctx, *pp_targetpath);
860                 if (!dfs_path) {
861                         return false;
862                 }
863                 *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
864         }
865
866         return true;
867 }
868
869 /********************************************************************
870 ********************************************************************/
871
872 bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
873                                 struct cli_state *cli,
874                                 const char *sharename,
875                                 char **pp_newserver,
876                                 char **pp_newshare )
877 {
878         CLIENT_DFS_REFERRAL *refs = NULL;
879         size_t num_refs;
880         uint16 consumed;
881         char *fullpath = NULL;
882         bool res;
883         uint16 cnum;
884         char *newextrapath = NULL;
885
886         if (!cli || !sharename) {
887                 return false;
888         }
889
890         cnum = cli->cnum;
891
892         /* special case.  never check for a referral on the IPC$ share */
893
894         if (strequal(sharename, "IPC$")) {
895                 return false;
896         }
897
898         /* send a trans2_query_path_info to check for a referral */
899
900         fullpath = talloc_asprintf(ctx, "\\%s\\%s", cli->desthost, sharename );
901         if (!fullpath) {
902                 return false;
903         }
904
905         /* check for the referral */
906
907         if (!cli_send_tconX(cli, "IPC$", "IPC", NULL, 0)) {
908                 return false;
909         }
910
911         res = cli_dfs_get_referral(cli, fullpath, &refs, &num_refs, &consumed);
912
913         if (!cli_tdis(cli)) {
914                 SAFE_FREE(refs);
915                 return false;
916         }
917
918         cli->cnum = cnum;
919
920         if (!res || !num_refs) {
921                 SAFE_FREE(refs);
922                 return false;
923         }
924
925         split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
926                         pp_newshare, &newextrapath );
927
928         SAFE_FREE(refs);
929
930         if (!pp_newserver || !pp_newshare) {
931                 return false;
932         }
933
934         /* check that this is not a self-referral */
935
936         if (strequal(cli->desthost, *pp_newserver) &&
937                         strequal(sharename, *pp_newshare)) {
938                 return false;
939         }
940
941         return true;
942 }