r21251: Okay, after Jeremy has kindly tested this, check it in :-)
[samba.git] / source3 / smbd / msdfs.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    MSDfs services for Samba
5    Copyright (C) Shirish Kalele 2000
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 */
22
23 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
25
26 extern uint32 global_client_caps;
27
28 /**********************************************************************
29   Parse the pathname  of the form \hostname\service\reqpath
30   into the dfs_path structure 
31  **********************************************************************/
32
33 static BOOL parse_dfs_path(const char *pathname, struct dfs_path *pdp)
34 {
35         pstring pathname_local;
36         char *p, *temp;
37
38         pstrcpy(pathname_local,pathname);
39         p = temp = pathname_local;
40
41         ZERO_STRUCTP(pdp);
42
43         trim_char(temp,'\\','\\');
44         DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
45
46         /* now tokenize */
47         /* parse out hostname */
48         p = strchr_m(temp,'\\');
49         if(p == NULL) {
50                 return False;
51         }
52         *p = '\0';
53         pstrcpy(pdp->hostname,temp);
54         DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
55
56         /* parse out servicename */
57         temp = p+1;
58         p = strchr_m(temp,'\\');
59         if(p == NULL) {
60                 pstrcpy(pdp->servicename,temp);
61                 pdp->reqpath[0] = '\0';
62                 return True;
63         }
64         *p = '\0';
65         pstrcpy(pdp->servicename,temp);
66         DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
67
68         /* rest is reqpath */
69         check_path_syntax(pdp->reqpath, p+1);
70
71         DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
72         return True;
73 }
74
75 /**********************************************************************
76  Parse the pathname  of the form /hostname/service/reqpath
77  into the dfs_path structure 
78  This code is dependent on the fact that check_path_syntax() will
79  convert '\\' characters to '/'.
80  When POSIX pathnames have been selected this doesn't happen, so we
81  must look for the unaltered separator of '\\' instead of the modified '/'.
82  JRA.
83  **********************************************************************/
84
85 static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path *pdp, BOOL allow_wcards)
86 {
87         pstring pathname_local;
88         char *p,*temp;
89         const char sepchar = lp_posix_pathnames() ? '\\' : '/';
90
91         pstrcpy(pathname_local,pathname);
92         p = temp = pathname_local;
93
94         ZERO_STRUCTP(pdp);
95
96         trim_char(temp,sepchar,sepchar);
97         DEBUG(10,("temp in parse_processed_dfs_path: .%s. after trimming \\'s\n",temp));
98
99         /* now tokenize */
100         /* parse out hostname */
101         p = strchr_m(temp,sepchar);
102         if(p == NULL) {
103                 return False;
104         }
105         *p = '\0';
106         pstrcpy(pdp->hostname,temp);
107         DEBUG(10,("parse_processed_dfs_path: hostname: %s\n",pdp->hostname));
108
109         /* parse out servicename */
110         temp = p+1;
111         p = strchr_m(temp,sepchar);
112         if(p == NULL) {
113                 pstrcpy(pdp->servicename,temp);
114                 pdp->reqpath[0] = '\0';
115                 return True;
116         }
117         *p = '\0';
118         pstrcpy(pdp->servicename,temp);
119         DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename));
120
121         /* rest is reqpath */
122         if (allow_wcards) {
123                 BOOL path_contains_wcard;
124                 check_path_syntax_wcard(pdp->reqpath, p+1, &path_contains_wcard);
125         } else {
126                 check_path_syntax(pdp->reqpath, p+1);
127         }
128
129         DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath));
130         return True;
131 }
132
133 /********************************************************
134  Fake up a connection struct for the VFS layer.
135  Note this CHANGES CWD !!!! JRA.
136 *********************************************************/
137
138 static BOOL create_conn_struct(connection_struct *conn, int snum, char *path)
139 {
140         pstring connpath;
141
142         ZERO_STRUCTP(conn);
143
144         pstrcpy(connpath, path);
145         pstring_sub(connpath , "%S", lp_servicename(snum));
146
147         /* needed for smbd_vfs_init() */
148         
149         if ( (conn->mem_ctx=talloc_init("connection_struct")) == NULL ) {
150                 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
151                 return False;
152         }
153
154         if (!(conn->params = TALLOC_P(conn->mem_ctx, struct share_params))) {
155                 DEBUG(0, ("TALLOC failed\n"));
156                 return False;
157         }
158         
159         conn->params->service = snum;
160         
161         set_conn_connectpath(conn, connpath);
162
163         if (!smbd_vfs_init(conn)) {
164                 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
165                 conn_free_internal(conn);
166                 return False;
167         }
168
169         /*
170          * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
171          * share as the anonymous user. If we try to chdir as that user we will
172          * fail.... WTF ? JRA.
173          */
174
175         if (vfs_ChDir(conn,conn->connectpath) != 0) {
176                 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
177                                         conn->connectpath, strerror(errno) ));
178                 conn_free_internal(conn);
179                 return False;
180         }
181
182         return True;
183 }
184
185 /**********************************************************************
186  Parse the contents of a symlink to verify if it is an msdfs referral
187  A valid referral is of the form: msdfs:server1\share1,server2\share2
188  talloc CTX can be NULL here if preflist and refcount pointers are null.
189  **********************************************************************/
190
191 static BOOL parse_symlink(TALLOC_CTX *ctx, char *buf, struct referral **preflist, int *refcount)
192 {
193         pstring temp;
194         char *prot;
195         char *alt_path[MAX_REFERRAL_COUNT];
196         int count = 0, i;
197         struct referral *reflist;
198
199         pstrcpy(temp,buf);
200   
201         prot = strtok(temp,":");
202
203         if (!strequal(prot, "msdfs")) {
204                 return False;
205         }
206
207         /* No referral list requested. Just yes/no. */
208         if (!preflist) {
209                 return True;
210         }
211
212         if (!ctx) {
213                 DEBUG(0,("parse_symlink: logic error. TALLOC_CTX should not be null.\n"));
214                 return True;
215         }
216
217         /* parse out the alternate paths */
218         while((count<MAX_REFERRAL_COUNT) &&
219               ((alt_path[count] = strtok(NULL,",")) != NULL)) {
220                 count++;
221         }
222
223         DEBUG(10,("parse_symlink: count=%d\n", count));
224
225         reflist = *preflist = TALLOC_ARRAY(ctx, struct referral, count);
226         if(reflist == NULL) {
227                 DEBUG(0,("parse_symlink: talloc failed!\n"));
228                 return False;
229         }
230         
231         for(i=0;i<count;i++) {
232                 char *p;
233
234                 /* replace all /'s in the alternate path by a \ */
235                 for(p = alt_path[i]; *p && ((p = strchr_m(p,'/'))!=NULL); p++) {
236                         *p = '\\'; 
237                 }
238
239                 /* Remove leading '\\'s */
240                 p = alt_path[i];
241                 while (*p && (*p == '\\')) {
242                         p++;
243                 }
244
245                 pstrcpy(reflist[i].alternate_path, "\\");
246                 pstrcat(reflist[i].alternate_path, p);
247                 reflist[i].proximity = 0;
248                 reflist[i].ttl = REFERRAL_TTL;
249                 DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
250         }
251
252         if(refcount) {
253                 *refcount = count;
254         }
255
256         return True;
257 }
258  
259 /**********************************************************************
260  Returns true if the unix path is a valid msdfs symlink
261  talloc CTX can be NULL here if reflistp and refcnt pointers are null.
262  **********************************************************************/
263
264 BOOL is_msdfs_link(TALLOC_CTX *ctx, connection_struct *conn, const char *path,
265                    struct referral **reflistp, int *refcnt,
266                    SMB_STRUCT_STAT *sbufp)
267 {
268         SMB_STRUCT_STAT st;
269         pstring referral;
270         int referral_len = 0;
271
272         if (!path || !conn) {
273                 return False;
274         }
275
276         if (sbufp == NULL) {
277                 sbufp = &st;
278         }
279
280         if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
281                 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
282                 return False;
283         }
284   
285         if (S_ISLNK(sbufp->st_mode)) {
286                 /* open the link and read it */
287                 referral_len = SMB_VFS_READLINK(conn, path, referral, sizeof(pstring)-1);
288                 if (referral_len == -1) {
289                         DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
290                         return False;
291                 }
292
293                 referral[referral_len] = '\0';
294                 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
295                 if (parse_symlink(ctx, referral, reflistp, refcnt)) {
296                         return True;
297                 }
298         }
299         return False;
300 }
301
302 /*****************************************************************
303  Used by other functions to decide if a dfs path is remote,
304 and to get the list of referred locations for that remote path.
305  
306 findfirst_flag: For findfirsts, dfs links themselves are not
307 redirected, but paths beyond the links are. For normal smb calls,
308 even dfs links need to be redirected.
309
310 self_referralp: clients expect a dfs referral for the same share when
311 they request referrals for dfs roots on a server. 
312
313 consumedcntp: how much of the dfs path is being redirected. the client
314 should try the remaining path on the redirected server.
315
316 TALLOC_CTX can be NULL here if struct referral **reflistpp, int *refcntp
317 are also NULL.
318 *****************************************************************/
319
320 static BOOL resolve_dfs_path(TALLOC_CTX *ctx,
321                         const char *dfspath, 
322                         struct dfs_path *dp, 
323                         connection_struct *conn,
324                         BOOL search_flag, 
325                         struct referral **reflistpp,
326                         int *refcntp,
327                         BOOL *self_referralp,
328                         int *consumedcntp)
329 {
330         pstring localpath;
331         int consumed_level = 1;
332         char *p;
333         SMB_STRUCT_STAT sbuf;
334         NTSTATUS status;
335         pstring reqpath;
336
337         if (!dp || !conn) {
338                 DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
339                 return False;
340         }
341
342         if (!ctx && (reflistpp || refcntp)) {
343                 DEBUG(0,("resolve_dfs_path: logic error. TALLOC_CTX must not be NULL.\n"));
344         }
345
346         if (dp->reqpath[0] == '\0') {
347                 if (self_referralp) {
348                         DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
349                         *self_referralp = True;
350                 }
351                 return False;
352         }
353
354         DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath));
355
356         /* 
357          * Note the unix path conversion here we're doing we can
358          * throw away. We're looking for a symlink for a dfs
359          * resolution, if we don't find it we'll do another
360          * unix_convert later in the codepath.
361          * If we needed to remember what we'd resolved in
362          * dp->reqpath (as the original code did) we'd
363          * pstrcpy(localhost, dp->reqpath) on any code
364          * path below that returns True - but I don't
365          * think this is needed. JRA.
366          */
367
368         pstrcpy(localpath, dp->reqpath);
369
370         status = unix_convert(conn, localpath, False, NULL, &sbuf);
371         if (!NT_STATUS_IS_OK(status)) {
372                 return False;
373         }
374
375         /* check if need to redirect */
376         if (is_msdfs_link(ctx, conn, localpath, reflistpp, refcntp, NULL)) {
377                 if ( search_flag ) {
378                         DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
379                                  "for dfs link %s.\n", dfspath));
380                         return False;
381                 }
382
383                 DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n", dfspath));
384                 if (consumedcntp) {
385                         *consumedcntp = strlen(dfspath);
386                 }
387                 return True;
388         }
389
390         /* redirect if any component in the path is a link */
391         pstrcpy(reqpath, localpath);
392         p = strrchr_m(reqpath, '/');
393         while (p) {
394                 *p = '\0';
395                 pstrcpy(localpath, reqpath);
396                 if (is_msdfs_link(ctx, conn, localpath, reflistpp, refcntp, NULL)) {
397                         DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
398
399                         /* To find the path consumed, we truncate the original
400                            DFS pathname passed to use to remove the last
401                            component. The length of the resulting string is
402                            the path consumed 
403                         */
404                         
405                         if (consumedcntp) {
406                                 char *q;
407                                 pstring buf;
408                                 pstrcpy(buf, dfspath);
409                                 trim_char(buf, '\0', '\\');
410                                 for (; consumed_level; consumed_level--) {
411                                         q = strrchr_m(buf, '\\');
412                                         if (q) {
413                                                 *q = 0;
414                                         }
415                                 }
416                                 *consumedcntp = strlen(buf);
417                                 DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
418                         }
419                         
420                         return True;
421                 }
422                 p = strrchr_m(reqpath, '/');
423                 consumed_level++;
424         }
425
426         return False;
427 }
428
429 /*****************************************************************
430   Decides if a dfs pathname should be redirected or not.
431   If not, the pathname is converted to a tcon-relative local unix path
432
433   search_wcard_flag: this flag performs 2 functions bother related
434   to searches.  See resolve_dfs_path() and parse_processed_dfs_path()
435   for details.
436 *****************************************************************/
437
438 BOOL dfs_redirect( pstring pathname, connection_struct *conn, BOOL search_wcard_flag )
439 {
440         struct dfs_path dp;
441         
442         if (!conn || !pathname) {
443                 return False;
444         }
445
446         parse_processed_dfs_path(pathname, &dp, search_wcard_flag);
447
448         /* if dfs pathname for a non-dfs share, convert to tcon-relative
449            path and return false */
450         if (!lp_msdfs_root(SNUM(conn))) {
451                 pstrcpy(pathname, dp.reqpath);
452                 return False;
453         }
454         
455         if ( !( strequal(dp.servicename, lp_servicename(SNUM(conn))) 
456                 || ( strequal(dp.servicename, HOMES_NAME) 
457                      && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) 
458         {
459                 return False;
460         }
461
462         if (resolve_dfs_path(NULL, pathname, &dp, conn, search_wcard_flag,
463                              NULL, NULL, NULL, NULL)) {
464                 DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
465                 return True;
466         } else {
467                 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
468
469                 /* Form non-dfs tcon-relative path */
470                 pstrcpy(pathname, dp.reqpath);
471
472                 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", pathname));
473                 return False;
474         }
475
476         /* never reached */
477 }
478
479 /**********************************************************************
480  Return a self referral.
481 **********************************************************************/
482
483 static BOOL self_ref(TALLOC_CTX *ctx, const char *pathname, struct junction_map *jucn,
484                         int *consumedcntp, BOOL *self_referralp)
485 {
486         struct referral *ref;
487
488         if (self_referralp != NULL) {
489                 *self_referralp = True;
490         }
491
492         jucn->referral_count = 1;
493         if((ref = TALLOC_P(ctx, struct referral)) == NULL) {
494                 DEBUG(0,("self_ref: malloc failed for referral\n"));
495                 return False;
496         }
497
498         pstrcpy(ref->alternate_path,pathname);
499         ref->proximity = 0;
500         ref->ttl = REFERRAL_TTL;
501         jucn->referral_list = ref;
502         if (consumedcntp) {
503                 *consumedcntp = strlen(pathname);
504         }
505
506         return True;
507 }
508
509 /**********************************************************************
510  Gets valid referrals for a dfs path and fills up the
511  junction_map structure.
512 **********************************************************************/
513
514 BOOL get_referred_path(TALLOC_CTX *ctx, const char *pathname, struct junction_map *jucn,
515                        int *consumedcntp, BOOL *self_referralp)
516 {
517         struct dfs_path dp;
518
519         struct connection_struct conns;
520         struct connection_struct *conn = &conns;
521         pstring conn_path;
522         int snum;
523         BOOL ret = False;
524         BOOL self_referral = False;
525
526         if (!pathname || !jucn) {
527                 return False;
528         }
529
530         ZERO_STRUCT(conns);
531
532         if (self_referralp) {
533                 *self_referralp = False;
534         } else {
535                 self_referralp = &self_referral;
536         }
537
538         parse_dfs_path(pathname, &dp);
539
540         /* Verify hostname in path */
541         if (!is_myname_or_ipaddr(dp.hostname)) {
542                 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
543                         dp.hostname, pathname));
544                 return False;
545         }
546
547         pstrcpy(jucn->service_name, dp.servicename);
548         pstrcpy(jucn->volume_name, dp.reqpath);
549
550         /* Verify the share is a dfs root */
551         snum = lp_servicenumber(jucn->service_name);
552         if(snum < 0) {
553                 if ((snum = find_service(jucn->service_name)) < 0) {
554                         return False;
555                 }
556         }
557
558         if (!lp_msdfs_root(snum)) {
559                 DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
560                          dp.servicename, pathname));
561                 goto out;
562         }
563
564         /*
565          * Self referrals are tested with a anonymous IPC connection and
566          * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
567          * to an empty string). create_conn_struct cd's into the directory and will
568          * fail if it cannot (as the anonymous user). Cope with this.
569          */
570
571         if (dp.reqpath[0] == '\0') {
572
573                 struct referral* ref;
574
575                 if (*lp_msdfs_proxy(snum) == '\0') {
576                         return self_ref(ctx, pathname, jucn, consumedcntp, self_referralp);
577                 }
578
579                 jucn->referral_count = 1;
580                 if ((ref = TALLOC_P(ctx, struct referral)) == NULL) {
581                         DEBUG(0, ("malloc failed for referral\n"));
582                         goto out;
583                 }
584
585                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
586                 if (dp.reqpath[0] != '\0') {
587                         pstrcat(ref->alternate_path, dp.reqpath);
588                 }
589                 ref->proximity = 0;
590                 ref->ttl = REFERRAL_TTL;
591                 jucn->referral_list = ref;
592                 if (consumedcntp) {
593                         *consumedcntp = strlen(pathname);
594                 }
595                 ret = True;
596                 goto out;
597         }
598
599         pstrcpy(conn_path, lp_pathname(snum));
600         if (!create_conn_struct(conn, snum, conn_path)) {
601                 return False;
602         }
603
604         /* If not remote & not a self referral, return False */
605         if (!resolve_dfs_path(ctx, pathname, &dp, conn, False, 
606                               &jucn->referral_list, &jucn->referral_count,
607                               self_referralp, consumedcntp)) {
608                 if (!*self_referralp) {
609                         DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
610                         goto out;
611                 }
612         }
613         
614         /* if self_referral, fill up the junction map */
615         if (*self_referralp) {
616                 if (self_ref(ctx, pathname, jucn, consumedcntp, self_referralp) == False) {
617                         goto out;
618                 }
619         }
620         
621         ret = True;
622
623 out:
624
625         conn_free_internal(conn);
626         return ret;
627 }
628
629 static int setup_ver2_dfs_referral(char *pathname, char **ppdata, 
630                                    struct junction_map *junction,
631                                    int consumedcnt,
632                                    BOOL self_referral)
633 {
634         char* pdata = *ppdata;
635
636         unsigned char uni_requestedpath[1024];
637         int uni_reqpathoffset1,uni_reqpathoffset2;
638         int uni_curroffset;
639         int requestedpathlen=0;
640         int offset;
641         int reply_size = 0;
642         int i=0;
643
644         DEBUG(10,("setting up version2 referral\nRequested path:\n"));
645
646         requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
647                                        STR_TERMINATE);
648
649         if (DEBUGLVL(10)) {
650             dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
651         }
652
653         DEBUG(10,("ref count = %u\n",junction->referral_count));
654
655         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
656                         VERSION2_REFERRAL_SIZE * junction->referral_count;
657
658         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
659
660         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
661
662         reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
663                                         2 * requestedpathlen;
664         DEBUG(10,("reply_size: %u\n",reply_size));
665
666         /* add up the unicode lengths of all the referral paths */
667         for(i=0;i<junction->referral_count;i++) {
668                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
669                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
670         }
671
672         DEBUG(10,("reply_size = %u\n",reply_size));
673         /* add the unexplained 0x16 bytes */
674         reply_size += 0x16;
675
676         pdata = (char *)SMB_REALLOC(pdata,reply_size);
677         if(pdata == NULL) {
678                 DEBUG(0,("malloc failed for Realloc!\n"));
679                 return -1;
680         }
681         *ppdata = pdata;
682
683         /* copy in the dfs requested paths.. required for offset calculations */
684         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
685         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
686
687         /* create the header */
688         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
689         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
690         if(self_referral) {
691                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
692         } else {
693                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
694         }
695
696         offset = 8;
697         /* add the referral elements */
698         for(i=0;i<junction->referral_count;i++) {
699                 struct referral* ref = &junction->referral_list[i];
700                 int unilen;
701
702                 SSVAL(pdata,offset,2); /* version 2 */
703                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
704                 if(self_referral) {
705                         SSVAL(pdata,offset+4,1);
706                 } else {
707                         SSVAL(pdata,offset+4,0);
708                 }
709                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
710                 SIVAL(pdata,offset+8,ref->proximity);
711                 SIVAL(pdata,offset+12,ref->ttl);
712
713                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
714                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
715                 /* copy referred path into current offset */
716                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
717                                      sizeof(pstring), STR_UNICODE);
718
719                 SSVAL(pdata,offset+20,uni_curroffset-offset);
720
721                 uni_curroffset += unilen;
722                 offset += VERSION2_REFERRAL_SIZE;
723         }
724         /* add in the unexplained 22 (0x16) bytes at the end */
725         memset(pdata+uni_curroffset,'\0',0x16);
726         return reply_size;
727 }
728
729 static int setup_ver3_dfs_referral(char *pathname, char **ppdata, 
730                                    struct junction_map *junction,
731                                    int consumedcnt,
732                                    BOOL self_referral)
733 {
734         char* pdata = *ppdata;
735
736         unsigned char uni_reqpath[1024];
737         int uni_reqpathoffset1, uni_reqpathoffset2;
738         int uni_curroffset;
739         int reply_size = 0;
740
741         int reqpathlen = 0;
742         int offset,i=0;
743         
744         DEBUG(10,("setting up version3 referral\n"));
745
746         reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
747         
748         if (DEBUGLVL(10)) {
749             dump_data(0, (char *) uni_reqpath,reqpathlen);
750         }
751
752         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
753         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
754         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
755
756         for(i=0;i<junction->referral_count;i++) {
757                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
758                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
759         }
760
761         pdata = (char *)SMB_REALLOC(pdata,reply_size);
762         if(pdata == NULL) {
763                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
764                 return -1;
765         }
766         *ppdata = pdata;
767
768         /* create the header */
769         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
770         SSVAL(pdata,2,junction->referral_count); /* number of referral */
771         if(self_referral) {
772                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
773         } else {
774                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
775         }
776         
777         /* copy in the reqpaths */
778         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
779         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
780         
781         offset = 8;
782         for(i=0;i<junction->referral_count;i++) {
783                 struct referral* ref = &(junction->referral_list[i]);
784                 int unilen;
785
786                 SSVAL(pdata,offset,3); /* version 3 */
787                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
788                 if(self_referral) {
789                         SSVAL(pdata,offset+4,1);
790                 } else {
791                         SSVAL(pdata,offset+4,0);
792                 }
793
794                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
795                 SIVAL(pdata,offset+8,ref->ttl);
796             
797                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
798                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
799                 /* copy referred path into current offset */
800                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
801                                      sizeof(pstring), STR_UNICODE | STR_TERMINATE);
802                 SSVAL(pdata,offset+16,uni_curroffset-offset);
803                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
804                 memset(pdata+offset+18,'\0',16);
805
806                 uni_curroffset += unilen;
807                 offset += VERSION3_REFERRAL_SIZE;
808         }
809         return reply_size;
810 }
811
812 /******************************************************************
813  Set up the Dfs referral for the dfs pathname
814 ******************************************************************/
815
816 int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char **ppdata)
817 {
818         struct junction_map junction;
819         int consumedcnt;
820         BOOL self_referral = False;
821         pstring buf;
822         int reply_size = 0;
823         char *pathnamep = pathname;
824         TALLOC_CTX *ctx;
825
826         if (!(ctx=talloc_init("setup_dfs_referral"))) {
827                 return -1;
828         }
829
830         ZERO_STRUCT(junction);
831
832         /* get the junction entry */
833         if (!pathnamep) {
834                 talloc_destroy(ctx);
835                 return -1;
836         }
837
838         /* Trim pathname sent by client so it begins with only one backslash.
839            Two backslashes confuse some dfs clients
840          */
841         while (pathnamep[0] == '\\' && pathnamep[1] == '\\') {
842                 pathnamep++;
843         }
844
845         pstrcpy(buf, pathnamep);
846         /* The following call can change cwd. */
847         if (!get_referred_path(ctx, buf, &junction, &consumedcnt, &self_referral)) {
848                 vfs_ChDir(orig_conn,orig_conn->connectpath);
849                 talloc_destroy(ctx);
850                 return -1;
851         }
852         vfs_ChDir(orig_conn,orig_conn->connectpath);
853         
854         if (!self_referral) {
855                 pathnamep[consumedcnt] = '\0';
856
857                 if( DEBUGLVL( 3 ) ) {
858                         int i=0;
859                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
860                         for(i=0;i<junction.referral_count;i++)
861                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
862                         dbgtext(".\n");
863                 }
864         }
865
866         /* create the referral depeding on version */
867         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
868
869         if (max_referral_level < 2) {
870                 max_referral_level = 2;
871         }
872         if (max_referral_level > 3) {
873                 max_referral_level = 3;
874         }
875
876         switch(max_referral_level) {
877         case 2:
878                 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
879                                                      consumedcnt, self_referral);
880                 break;
881         case 3:
882                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
883                                                      consumedcnt, self_referral);
884                 break;
885         default:
886                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
887                 talloc_destroy(ctx);
888                 return -1;
889         }
890       
891         if (DEBUGLVL(10)) {
892                 DEBUGADD(0,("DFS Referral pdata:\n"));
893                 dump_data(0,*ppdata,reply_size);
894         }
895
896         talloc_destroy(ctx);
897         return reply_size;
898 }
899
900 /**********************************************************************
901  The following functions are called by the NETDFS RPC pipe functions
902  **********************************************************************/
903
904 /*********************************************************************
905  Creates a junction structure from a Dfs pathname
906 **********************************************************************/
907
908 BOOL create_junction(const char *pathname, struct junction_map *jucn)
909 {
910         struct dfs_path dp;
911  
912         parse_dfs_path(pathname,&dp);
913
914         /* check if path is dfs : validate first token */
915         if (!is_myname_or_ipaddr(dp.hostname)) {
916                 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
917                         dp.hostname, pathname));
918                 return False;
919         }
920
921         /* Check for a non-DFS share */
922         if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
923                 DEBUG(4,("create_junction: %s is not an msdfs root.\n", dp.servicename));
924                 return False;
925         }
926
927         pstrcpy(jucn->service_name,dp.servicename);
928         pstrcpy(jucn->volume_name,dp.reqpath);
929         pstrcpy(jucn->comment, lp_comment(lp_servicenumber(dp.servicename)));
930         return True;
931 }
932
933 /**********************************************************************
934  Forms a valid Unix pathname from the junction 
935  **********************************************************************/
936
937 static BOOL junction_to_local_path(struct junction_map *jucn, char *path,
938                                    int max_pathlen, connection_struct *conn)
939 {
940         int snum;
941         pstring conn_path;
942
943         if(!path || !jucn) {
944                 return False;
945         }
946
947         snum = lp_servicenumber(jucn->service_name);
948         if(snum < 0) {
949                 return False;
950         }
951
952         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
953         safe_strcat(path, "/", max_pathlen-1);
954         safe_strcat(path, jucn->volume_name, max_pathlen-1);
955
956         pstrcpy(conn_path, lp_pathname(snum));
957         if (!create_conn_struct(conn, snum, conn_path)) {
958                 return False;
959         }
960
961         return True;
962 }
963
964 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
965 {
966         pstring path;
967         pstring msdfs_link;
968         connection_struct conns;
969         connection_struct *conn = &conns;
970         int i=0;
971         BOOL insert_comma = False;
972         BOOL ret = False;
973
974         ZERO_STRUCT(conns);
975
976         if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
977                 return False;
978         }
979   
980         /* form the msdfs_link contents */
981         pstrcpy(msdfs_link, "msdfs:");
982         for(i=0; i<jucn->referral_count; i++) {
983                 char* refpath = jucn->referral_list[i].alternate_path;
984       
985                 trim_char(refpath, '\\', '\\');
986                 if(*refpath == '\0') {
987                         if (i == 0) {
988                                 insert_comma = False;
989                         }
990                         continue;
991                 }
992                 if (i > 0 && insert_comma) {
993                         pstrcat(msdfs_link, ",");
994                 }
995
996                 pstrcat(msdfs_link, refpath);
997                 if (!insert_comma) {
998                         insert_comma = True;
999                 }
1000         }
1001
1002         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
1003
1004         if(exists) {
1005                 if(SMB_VFS_UNLINK(conn,path)!=0) {
1006                         goto out;
1007                 }
1008         }
1009
1010         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
1011                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
1012                                 path, msdfs_link, strerror(errno)));
1013                 goto out;
1014         }
1015         
1016         
1017         ret = True;
1018         
1019 out:
1020
1021         conn_free_internal(conn);
1022         return ret;
1023 }
1024
1025 BOOL remove_msdfs_link(struct junction_map *jucn)
1026 {
1027         pstring path;
1028         connection_struct conns;
1029         connection_struct *conn = &conns;
1030         BOOL ret = False;
1031
1032         ZERO_STRUCT(conns);
1033
1034         if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1035                 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1036                         ret = True;
1037                 }
1038                 talloc_destroy( conn->mem_ctx );
1039         }
1040
1041         conn_free_internal(conn);
1042         return ret;
1043 }
1044
1045 static int form_junctions(TALLOC_CTX *ctx, int snum, struct junction_map *jucn, int jn_remain)
1046 {
1047         int cnt = 0;
1048         SMB_STRUCT_DIR *dirp;
1049         char* dname;
1050         pstring connect_path;
1051         char* service_name = lp_servicename(snum);
1052         connection_struct conn;
1053         struct referral *ref = NULL;
1054  
1055         ZERO_STRUCT(conn);
1056
1057         if (jn_remain <= 0) {
1058                 return 0;
1059         }
1060
1061         pstrcpy(connect_path,lp_pathname(snum));
1062
1063         if(*connect_path == '\0') {
1064                 return 0;
1065         }
1066
1067         /*
1068          * Fake up a connection struct for the VFS layer.
1069          */
1070
1071         if (!create_conn_struct(&conn, snum, connect_path)) {
1072                 return 0;
1073         }
1074
1075         /* form a junction for the msdfs root - convention 
1076            DO NOT REMOVE THIS: NT clients will not work with us
1077            if this is not present
1078         */ 
1079         pstrcpy(jucn[cnt].service_name, service_name);
1080         jucn[cnt].volume_name[0] = '\0';
1081         jucn[cnt].referral_count = 1;
1082
1083         ref = jucn[cnt].referral_list = TALLOC_P(ctx, struct referral);
1084         if (jucn[cnt].referral_list == NULL) {
1085                 DEBUG(0, ("Malloc failed!\n"));
1086                 goto out;
1087         }
1088
1089         ref->proximity = 0;
1090         ref->ttl = REFERRAL_TTL;
1091         if (*lp_msdfs_proxy(snum) != '\0') {
1092                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1093                 cnt++;
1094                 goto out;
1095         }
1096                 
1097         slprintf(ref->alternate_path, sizeof(pstring)-1,
1098                  "\\\\%s\\%s", get_local_machine_name(), service_name);
1099         cnt++;
1100
1101         /* Now enumerate all dfs links */
1102         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1103         if(!dirp) {
1104                 goto out;
1105         }
1106
1107         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1108                 if (cnt >= jn_remain) {
1109                         SMB_VFS_CLOSEDIR(&conn,dirp);
1110                         DEBUG(2, ("ran out of MSDFS junction slots"));
1111                         goto out;
1112                 }
1113                 if (is_msdfs_link(ctx, &conn, dname, &jucn[cnt].referral_list,
1114                                   &jucn[cnt].referral_count, NULL)) {
1115                         pstrcpy(jucn[cnt].service_name, service_name);
1116                         pstrcpy(jucn[cnt].volume_name, dname);
1117                         cnt++;
1118                 }
1119         }
1120         
1121         SMB_VFS_CLOSEDIR(&conn,dirp);
1122
1123 out:
1124
1125         conn_free_internal(&conn);
1126         return cnt;
1127 }
1128
1129 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1130 {
1131         int i=0;
1132         int sharecount = 0;
1133         int jn_count = 0;
1134
1135         if(!lp_host_msdfs()) {
1136                 return 0;
1137         }
1138
1139         /* Ensure all the usershares are loaded. */
1140         become_root();
1141         load_registry_shares();
1142         sharecount = load_usershare_shares();
1143         unbecome_root();
1144
1145         for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1146                 if(lp_msdfs_root(i)) {
1147                         jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1148                 }
1149         }
1150         return jn_count;
1151 }