Remove PSTRING_LEN from smbd/ nmbd/.
[samba.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                                         const char *server,
270                                         const char *share,
271                                         bool show_hdr)
272 {
273         struct client_connection *node;
274
275         /* NB This must be the null context here... JRA. */
276         node = TALLOC_ZERO_ARRAY(NULL, struct client_connection, 1);
277         if (!node) {
278                 return NULL;
279         }
280
281         node->cli = do_connect(ctx, server, share, show_hdr);
282
283         if ( !node->cli ) {
284                 TALLOC_FREE( node );
285                 return NULL;
286         }
287
288         DLIST_ADD( connections, node );
289
290         cli_cm_set_mntpoint(node->cli, "");
291
292         return node->cli;
293
294 }
295
296 /********************************************************************
297  Return a connection to a server.
298 ********************************************************************/
299
300 static struct cli_state *cli_cm_find(const char *server, const char *share)
301 {
302         struct client_connection *p;
303
304         for (p=connections; p; p=p->next) {
305                 if ( strequal(server, p->cli->desthost) &&
306                                 strequal(share,p->cli->share)) {
307                         return p->cli;
308                 }
309         }
310
311         return NULL;
312 }
313
314 /****************************************************************************
315  Open a client connection to a \\server\share.  Set's the current *cli
316  global variable as a side-effect (but only if the connection is successful).
317 ****************************************************************************/
318
319 struct cli_state *cli_cm_open(TALLOC_CTX *ctx,
320                                 const char *server,
321                                 const char *share,
322                                 bool show_hdr)
323 {
324         struct cli_state *c;
325
326         /* try to reuse an existing connection */
327
328         c = cli_cm_find(server, share);
329         if (!c) {
330                 c = cli_cm_connect(ctx, server, share, show_hdr);
331         }
332
333         return c;
334 }
335
336 /****************************************************************************
337 ****************************************************************************/
338
339 void cli_cm_shutdown(void)
340 {
341         struct client_connection *p, *x;
342
343         for (p=connections; p;) {
344                 cli_shutdown(p->cli);
345                 x = p;
346                 p = p->next;
347
348                 TALLOC_FREE(x);
349         }
350
351         connections = NULL;
352         return;
353 }
354
355 /****************************************************************************
356 ****************************************************************************/
357
358 void cli_cm_display(void)
359 {
360         struct client_connection *p;
361         int i;
362
363         for ( p=connections,i=0; p; p=p->next,i++ ) {
364                 d_printf("%d:\tserver=%s, share=%s\n",
365                         i, p->cli->desthost, p->cli->share );
366         }
367 }
368
369 /****************************************************************************
370 ****************************************************************************/
371
372 static void cm_set_password(const char *newpass)
373 {
374         SAFE_FREE(cm_creds.password);
375         cm_creds.password = SMB_STRDUP(newpass);
376         if (cm_creds.password) {
377                 cm_creds.got_pass = true;
378         }
379 }
380
381 void cli_cm_set_credentials(struct user_auth_info *user)
382 {
383         SAFE_FREE(cm_creds.username);
384         cm_creds.username = SMB_STRDUP(user->username);
385
386         if (user->got_pass) {
387                 cm_set_password(user->password);
388         }
389
390         cm_creds.use_kerberos = user->use_kerberos;
391         cm_creds.signing_state = user->signing_state;
392 }
393
394 /****************************************************************************
395 ****************************************************************************/
396
397 void cli_cm_set_port(int port_number)
398 {
399         port = port_number;
400 }
401
402 /****************************************************************************
403 ****************************************************************************/
404
405 void cli_cm_set_dest_name_type(int type)
406 {
407         name_type = type;
408 }
409
410 /****************************************************************************
411 ****************************************************************************/
412
413 void cli_cm_set_dest_ss(struct sockaddr_storage *pss)
414 {
415         dest_ss = *pss;
416         have_ip = true;
417 }
418
419 /**********************************************************************
420  split a dfs path into the server, share name, and extrapath components
421 **********************************************************************/
422
423 static void split_dfs_path(TALLOC_CTX *ctx,
424                                 const char *nodepath,
425                                 char **pp_server,
426                                 char **pp_share,
427                                 char **pp_extrapath)
428 {
429         char *p, *q;
430         char *path;
431
432         *pp_server = NULL;
433         *pp_share = NULL;
434         *pp_extrapath = NULL;
435
436         path = talloc_strdup(ctx, nodepath);
437         if (!path) {
438                 return;
439         }
440
441         if ( path[0] != '\\' ) {
442                 return;
443         }
444
445         p = strchr_m( path + 1, '\\' );
446         if ( !p ) {
447                 return;
448         }
449
450         *p = '\0';
451         p++;
452
453         /* Look for any extra/deep path */
454         q = strchr_m(p, '\\');
455         if (q != NULL) {
456                 *q = '\0';
457                 q++;
458                 *pp_extrapath = talloc_strdup(ctx, q);
459         } else {
460                 *pp_extrapath = talloc_strdup(ctx, "");
461         }
462
463         *pp_share = talloc_strdup(ctx, p);
464         *pp_server = talloc_strdup(ctx, &path[1]);
465 }
466
467 /****************************************************************************
468  Return the original path truncated at the directory component before
469  the first wildcard character. Trust the caller to provide a NULL
470  terminated string
471 ****************************************************************************/
472
473 static char *clean_path(TALLOC_CTX *ctx, const char *path)
474 {
475         size_t len;
476         char *p1, *p2, *p;
477         char *path_out;
478
479         /* No absolute paths. */
480         while (IS_DIRECTORY_SEP(*path)) {
481                 path++;
482         }
483
484         path_out = talloc_strdup(ctx, path);
485         if (!path_out) {
486                 return NULL;
487         }
488
489         p1 = strchr_m(path_out, '*');
490         p2 = strchr_m(path_out, '?');
491
492         if (p1 || p2) {
493                 if (p1 && p2) {
494                         p = MIN(p1,p2);
495                 } else if (!p1) {
496                         p = p2;
497                 } else {
498                         p = p1;
499                 }
500                 *p = '\0';
501
502                 /* Now go back to the start of this component. */
503                 p1 = strrchr_m(path_out, '/');
504                 p2 = strrchr_m(path_out, '\\');
505                 p = MAX(p1,p2);
506                 if (p) {
507                         *p = '\0';
508                 }
509         }
510
511         /* Strip any trailing separator */
512
513         len = strlen(path_out);
514         if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
515                 path_out[len-1] = '\0';
516         }
517
518         return path_out;
519 }
520
521 /****************************************************************************
522 ****************************************************************************/
523
524 static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
525                                         struct cli_state *cli,
526                                         const char *dir)
527 {
528         /* Ensure the extrapath doesn't start with a separator. */
529         while (IS_DIRECTORY_SEP(*dir)) {
530                 dir++;
531         }
532
533         return talloc_asprintf(ctx, "\\%s\\%s\\%s",
534                         cli->desthost, cli->share, dir);
535 }
536
537 /********************************************************************
538  check for dfs referral
539 ********************************************************************/
540
541 static bool cli_dfs_check_error( struct cli_state *cli, NTSTATUS status )
542 {
543         uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
544
545         /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
546
547         if (!((flgs2&FLAGS2_32_BIT_ERROR_CODES) &&
548                                 (flgs2&FLAGS2_UNICODE_STRINGS)))
549                 return false;
550
551         if (NT_STATUS_EQUAL(status, NT_STATUS(IVAL(cli->inbuf,smb_rcls))))
552                 return true;
553
554         return false;
555 }
556
557 /********************************************************************
558  Get the dfs referral link.
559 ********************************************************************/
560
561 bool cli_dfs_get_referral(struct cli_state *cli,
562                         const char *path,
563                         CLIENT_DFS_REFERRAL**refs,
564                         size_t *num_refs,
565                         uint16 *consumed)
566 {
567         unsigned int data_len = 0;
568         unsigned int param_len = 0;
569         uint16 setup = TRANSACT2_GET_DFS_REFERRAL;
570         char param[1024+2];
571         char *rparam=NULL, *rdata=NULL;
572         char *p;
573         size_t pathlen = 2*(strlen(path)+1);
574         uint16 num_referrals;
575         CLIENT_DFS_REFERRAL *referrals = NULL;
576
577         memset(param, 0, sizeof(param));
578         SSVAL(param, 0, 0x03);  /* max referral level */
579         p = &param[2];
580
581         p += clistr_push(cli, p, path, MIN(pathlen, sizeof(param)-2),
582                         STR_TERMINATE);
583         param_len = PTR_DIFF(p, param);
584
585         if (!cli_send_trans(cli, SMBtrans2,
586                 NULL,                        /* name */
587                 -1, 0,                          /* fid, flags */
588                 &setup, 1, 0,                   /* setup, length, max */
589                 param, param_len, 2,            /* param, length, max */
590                 NULL, 0, cli->max_xmit /* data, length, max */
591                 )) {
592                         return false;
593         }
594
595         if (!cli_receive_trans(cli, SMBtrans2,
596                 &rparam, &param_len,
597                 &rdata, &data_len)) {
598                         return false;
599         }
600
601         *consumed     = SVAL( rdata, 0 );
602         num_referrals = SVAL( rdata, 2 );
603
604         if ( num_referrals != 0 ) {
605                 uint16 ref_version;
606                 uint16 ref_size;
607                 int i;
608                 uint16 node_offset;
609
610                 referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL,
611                                 num_referrals);
612
613                 /* start at the referrals array */
614
615                 p = rdata+8;
616                 for ( i=0; i<num_referrals; i++ ) {
617                         ref_version = SVAL( p, 0 );
618                         ref_size    = SVAL( p, 2 );
619                         node_offset = SVAL( p, 16 );
620
621                         if ( ref_version != 3 ) {
622                                 p += ref_size;
623                                 continue;
624                         }
625
626                         referrals[i].proximity = SVAL( p, 8 );
627                         referrals[i].ttl       = SVAL( p, 10 );
628
629                         clistr_pull( cli, referrals[i].dfspath, p+node_offset,
630                                 sizeof(referrals[i].dfspath), -1,
631                                 STR_TERMINATE|STR_UNICODE );
632
633                         p += ref_size;
634                 }
635         }
636
637         *num_refs = num_referrals;
638         *refs = referrals;
639
640         SAFE_FREE(rdata);
641         SAFE_FREE(rparam);
642
643         return true;
644 }
645
646
647 /********************************************************************
648 ********************************************************************/
649
650 bool cli_resolve_path(TALLOC_CTX *ctx,
651                         const char *mountpt,
652                         struct cli_state *rootcli,
653                         const char *path,
654                         struct cli_state **targetcli,
655                         char **pp_targetpath)
656 {
657         CLIENT_DFS_REFERRAL *refs = NULL;
658         size_t num_refs;
659         uint16 consumed;
660         struct cli_state *cli_ipc = NULL;
661         char *dfs_path = NULL;
662         char *cleanpath = NULL;
663         char *extrapath = NULL;
664         int pathlen;
665         char *server = NULL;
666         char *share = NULL;
667         struct cli_state *newcli = NULL;
668         char *newpath = NULL;
669         char *newmount = NULL;
670         char *ppath = NULL;
671         SMB_STRUCT_STAT sbuf;
672         uint32 attributes;
673
674         if ( !rootcli || !path || !targetcli ) {
675                 return false;
676         }
677
678         /* Don't do anything if this is not a DFS root. */
679
680         if ( !rootcli->dfsroot) {
681                 *targetcli = rootcli;
682                 *pp_targetpath = talloc_strdup(ctx, path);
683                 if (!*pp_targetpath) {
684                         return false;
685                 }
686                 return true;
687         }
688
689         *targetcli = NULL;
690
691         /* Send a trans2_query_path_info to check for a referral. */
692
693         cleanpath = clean_path(ctx, path);
694         if (!cleanpath) {
695                 return false;
696         }
697
698         dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
699         if (!dfs_path) {
700                 return false;
701         }
702
703         if (cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes)) {
704                 /* This is an ordinary path, just return it. */
705                 *targetcli = rootcli;
706                 *pp_targetpath = talloc_strdup(ctx, path);
707                 if (!*pp_targetpath) {
708                         return false;
709                 }
710                 goto done;
711         }
712
713         /* Special case where client asked for a path that does not exist */
714
715         if (cli_dfs_check_error(rootcli, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
716                 *targetcli = rootcli;
717                 *pp_targetpath = talloc_strdup(ctx, path);
718                 if (!*pp_targetpath) {
719                         return false;
720                 }
721                 goto done;
722         }
723
724         /* We got an error, check for DFS referral. */
725
726         if (!cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED)) {
727                 return false;
728         }
729
730         /* Check for the referral. */
731
732         if (!(cli_ipc = cli_cm_open(ctx, rootcli->desthost, "IPC$", false))) {
733                 return false;
734         }
735
736         if (!cli_dfs_get_referral(cli_ipc, dfs_path, &refs,
737                         &num_refs, &consumed) || !num_refs) {
738                 return false;
739         }
740
741         /* Just store the first referral for now. */
742
743         split_dfs_path(ctx, refs[0].dfspath, &server, &share, &extrapath );
744         SAFE_FREE(refs);
745
746         if (!server || !share) {
747                 return false;
748         }
749
750         /* Make sure to recreate the original string including any wildcards. */
751
752         dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
753         if (!dfs_path) {
754                 return false;
755         }
756         pathlen = strlen(dfs_path)*2;
757         consumed = MIN(pathlen, consumed);
758         *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed/2]);
759         if (!*pp_targetpath) {
760                 return false;
761         }
762         dfs_path[consumed/2] = '\0';
763
764         /*
765          * *pp_targetpath is now the unconsumed part of the path.
766          * dfs_path is now the consumed part of the path
767          * (in \server\share\path format).
768          */
769
770         /* Open the connection to the target server & share */
771         if ((*targetcli = cli_cm_open(ctx, server, share, false)) == NULL) {
772                 d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
773                         server, share );
774                 return false;
775         }
776
777         if (extrapath && strlen(extrapath) > 0) {
778                 *pp_targetpath = talloc_asprintf(ctx,
779                                                 "%s%s",
780                                                 extrapath,
781                                                 *pp_targetpath);
782                 if (!*pp_targetpath) {
783                         return false;
784                 }
785         }
786
787         /* parse out the consumed mount path */
788         /* trim off the \server\share\ */
789
790         ppath = dfs_path;
791
792         if (*ppath != '\\') {
793                 d_printf("cli_resolve_path: "
794                         "dfs_path (%s) not in correct format.\n",
795                         dfs_path );
796                 return false;
797         }
798
799         ppath++; /* Now pointing at start of server name. */
800
801         if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
802                 return false;
803         }
804
805         ppath++; /* Now pointing at start of share name. */
806
807         if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
808                 return false;
809         }
810
811         ppath++; /* Now pointing at path component. */
812
813         newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
814         if (!newmount) {
815                 return false;
816         }
817
818         cli_cm_set_mntpoint(*targetcli, newmount);
819
820         /* Check for another dfs referral, note that we are not
821            checking for loops here. */
822
823         if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
824                 if (cli_resolve_path(ctx,
825                                         newmount,
826                                         *targetcli,
827                                         *pp_targetpath,
828                                         &newcli,
829                                         &newpath)) {
830                         /*
831                          * When cli_resolve_path returns true here it's always
832                          * returning the complete path in newpath, so we're done
833                          * here.
834                          */
835                         *targetcli = newcli;
836                         *pp_targetpath = newpath;
837                         return true;
838                 }
839         }
840
841   done:
842
843         /* If returning true ensure we return a dfs root full path. */
844         if ((*targetcli)->dfsroot) {
845                 dfs_path = talloc_strdup(ctx, *pp_targetpath);
846                 if (!dfs_path) {
847                         return false;
848                 }
849                 *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
850         }
851
852         return true;
853 }
854
855 /********************************************************************
856  Temporary hack - remove when pstring is dead. JRA.
857 ********************************************************************/
858
859 bool cli_resolve_path_pstring( const char *mountpt,
860                         struct cli_state *rootcli,
861                         const char *path,
862                         struct cli_state **targetcli,
863                         pstring targetpath)
864 {
865         char *tpath = NULL;
866         TALLOC_CTX *ctx = talloc_stackframe();
867         bool ret = cli_resolve_path(ctx,
868                                 mountpt,
869                                 rootcli,
870                                 path,
871                                 targetcli,
872                                 &tpath);
873         if (tpath) {
874                 pstrcpy(targetpath, tpath);
875         }
876         return ret;
877 }
878
879 /********************************************************************
880 ********************************************************************/
881
882 bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
883                                 struct cli_state *cli,
884                                 const char *sharename,
885                                 char **pp_newserver,
886                                 char **pp_newshare )
887 {
888         CLIENT_DFS_REFERRAL *refs = NULL;
889         size_t num_refs;
890         uint16 consumed;
891         char *fullpath = NULL;
892         bool res;
893         uint16 cnum;
894         char *newextrapath = NULL;
895
896         if (!cli || !sharename) {
897                 return false;
898         }
899
900         cnum = cli->cnum;
901
902         /* special case.  never check for a referral on the IPC$ share */
903
904         if (strequal(sharename, "IPC$")) {
905                 return false;
906         }
907
908         /* send a trans2_query_path_info to check for a referral */
909
910         fullpath = talloc_asprintf(ctx, "\\%s\\%s", cli->desthost, sharename );
911         if (!fullpath) {
912                 return false;
913         }
914
915         /* check for the referral */
916
917         if (!cli_send_tconX(cli, "IPC$", "IPC", NULL, 0)) {
918                 return false;
919         }
920
921         res = cli_dfs_get_referral(cli, fullpath, &refs, &num_refs, &consumed);
922
923         if (!cli_tdis(cli)) {
924                 SAFE_FREE(refs);
925                 return false;
926         }
927
928         cli->cnum = cnum;
929
930         if (!res || !num_refs) {
931                 SAFE_FREE(refs);
932                 return false;
933         }
934
935         split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
936                         pp_newshare, &newextrapath );
937
938         SAFE_FREE(refs);
939
940         if (!pp_newserver || !pp_newshare) {
941                 return false;
942         }
943
944         /* check that this is not a self-referral */
945
946         if (strequal(cli->desthost, *pp_newserver) &&
947                         strequal(sharename, *pp_newshare)) {
948                 return false;
949         }
950
951         return true;
952 }