r20718: Sync up the filename path parsing changes from SAMBA_3_0_24.
[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, 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, const char *dfspath, 
321                                                          struct dfs_path *dp, 
322                       connection_struct *conn, BOOL search_flag, 
323                       struct referral **reflistpp, int *refcntp,
324                       BOOL *self_referralp, int *consumedcntp)
325 {
326         pstring localpath;
327         int consumed_level = 1;
328         char *p;
329         SMB_STRUCT_STAT sbuf;
330         NTSTATUS status;
331         pstring reqpath;
332
333         if (!dp || !conn) {
334                 DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
335                 return False;
336         }
337
338         if (!ctx && (reflistpp || refcntp)) {
339                 DEBUG(0,("resolve_dfs_path: logic error. TALLOC_CTX must not be NULL.\n"));
340         }
341
342         if (dp->reqpath[0] == '\0') {
343                 if (self_referralp) {
344                         DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
345                         *self_referralp = True;
346                 }
347                 return False;
348         }
349
350         DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath));
351
352         status = unix_convert(conn, dp->reqpath, False, NULL, &sbuf);
353         /* Should we terminate on status != NT_STATUS_OK ???? */
354
355         /* JRA... should we strlower the last component here.... ? */
356         pstrcpy(localpath, dp->reqpath);
357
358         /* check if need to redirect */
359         if (is_msdfs_link(ctx, conn, localpath, reflistpp, refcntp, NULL)) {
360                 if ( search_flag ) {
361                         DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
362                                  "for dfs link %s.\n", dfspath));
363                         return False;
364                 }
365
366                 DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n", dfspath));
367                 if (consumedcntp) {
368                         *consumedcntp = strlen(dfspath);
369                 }
370                 return True;
371         }
372
373         /* redirect if any component in the path is a link */
374         pstrcpy(reqpath, dp->reqpath);
375         p = strrchr_m(reqpath, '/');
376         while (p) {
377                 *p = '\0';
378                 pstrcpy(localpath, reqpath);
379                 if (is_msdfs_link(ctx, conn, localpath, reflistpp, refcntp, NULL)) {
380                         DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
381
382                         /* To find the path consumed, we truncate the original
383                            DFS pathname passed to use to remove the last
384                            component. The length of the resulting string is
385                            the path consumed 
386                         */
387                         
388                         if (consumedcntp) {
389                                 char *q;
390                                 pstring buf;
391                                 pstrcpy(buf, dfspath);
392                                 trim_char(buf, '\0', '\\');
393                                 for (; consumed_level; consumed_level--) {
394                                         q = strrchr_m(buf, '\\');
395                                         if (q) {
396                                                 *q = 0;
397                                         }
398                                 }
399                                 *consumedcntp = strlen(buf);
400                                 DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
401                         }
402                         
403                         return True;
404                 }
405                 p = strrchr_m(reqpath, '/');
406                 consumed_level++;
407         }
408
409         return False;
410 }
411
412 /*****************************************************************
413   Decides if a dfs pathname should be redirected or not.
414   If not, the pathname is converted to a tcon-relative local unix path
415
416   search_wcard_flag: this flag performs 2 functions bother related
417   to searches.  See resolve_dfs_path() and parse_processed_dfs_path()
418   for details.
419 *****************************************************************/
420
421 BOOL dfs_redirect( pstring pathname, connection_struct *conn, BOOL search_wcard_flag )
422 {
423         struct dfs_path dp;
424         
425         if (!conn || !pathname) {
426                 return False;
427         }
428
429         parse_processed_dfs_path(pathname, &dp, search_wcard_flag);
430
431         /* if dfs pathname for a non-dfs share, convert to tcon-relative
432            path and return false */
433         if (!lp_msdfs_root(SNUM(conn))) {
434                 pstrcpy(pathname, dp.reqpath);
435                 return False;
436         }
437         
438         if ( !( strequal(dp.servicename, lp_servicename(SNUM(conn))) 
439                 || ( strequal(dp.servicename, HOMES_NAME) 
440                      && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) 
441         {
442                 return False;
443         }
444
445         if (resolve_dfs_path(NULL, pathname, &dp, conn, search_wcard_flag,
446                              NULL, NULL, NULL, NULL)) {
447                 DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
448                 return True;
449         } else {
450                 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
451                 
452                 /* Form non-dfs tcon-relative path */
453                 pstrcpy(pathname, dp.reqpath);
454                 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", pathname));
455                 return False;
456         }
457
458         /* never reached */
459 }
460
461 /**********************************************************************
462  Return a self referral.
463 **********************************************************************/
464
465 static BOOL self_ref(TALLOC_CTX *ctx, const char *pathname, struct junction_map *jucn,
466                         int *consumedcntp, BOOL *self_referralp)
467 {
468         struct referral *ref;
469
470         if (self_referralp != NULL) {
471                 *self_referralp = True;
472         }
473
474         jucn->referral_count = 1;
475         if((ref = TALLOC_P(ctx, struct referral)) == NULL) {
476                 DEBUG(0,("self_ref: malloc failed for referral\n"));
477                 return False;
478         }
479
480         pstrcpy(ref->alternate_path,pathname);
481         ref->proximity = 0;
482         ref->ttl = REFERRAL_TTL;
483         jucn->referral_list = ref;
484         if (consumedcntp) {
485                 *consumedcntp = strlen(pathname);
486         }
487
488         return True;
489 }
490
491 /**********************************************************************
492  Gets valid referrals for a dfs path and fills up the
493  junction_map structure.
494 **********************************************************************/
495
496 BOOL get_referred_path(TALLOC_CTX *ctx, const char *pathname, struct junction_map *jucn,
497                        int *consumedcntp, BOOL *self_referralp)
498 {
499         struct dfs_path dp;
500
501         struct connection_struct conns;
502         struct connection_struct *conn = &conns;
503         pstring conn_path;
504         int snum;
505         BOOL ret = False;
506         BOOL self_referral = False;
507
508         if (!pathname || !jucn) {
509                 return False;
510         }
511
512         ZERO_STRUCT(conns);
513
514         if (self_referralp) {
515                 *self_referralp = False;
516         } else {
517                 self_referralp = &self_referral;
518         }
519
520         parse_dfs_path(pathname, &dp);
521
522         /* Verify hostname in path */
523         if (!is_myname_or_ipaddr(dp.hostname)) {
524                 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
525                         dp.hostname, pathname));
526                 return False;
527         }
528
529         pstrcpy(jucn->service_name, dp.servicename);
530         pstrcpy(jucn->volume_name, dp.reqpath);
531
532         /* Verify the share is a dfs root */
533         snum = lp_servicenumber(jucn->service_name);
534         if(snum < 0) {
535                 if ((snum = find_service(jucn->service_name)) < 0) {
536                         return False;
537                 }
538         }
539
540         if (!lp_msdfs_root(snum)) {
541                 DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
542                          dp.servicename, pathname));
543                 goto out;
544         }
545
546         /*
547          * Self referrals are tested with a anonymous IPC connection and
548          * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
549          * to an empty string). create_conn_struct cd's into the directory and will
550          * fail if it cannot (as the anonymous user). Cope with this.
551          */
552
553         if (dp.reqpath[0] == '\0') {
554
555                 struct referral* ref;
556
557                 if (*lp_msdfs_proxy(snum) == '\0') {
558                         return self_ref(ctx, pathname, jucn, consumedcntp, self_referralp);
559                 }
560
561                 jucn->referral_count = 1;
562                 if ((ref = TALLOC_P(ctx, struct referral)) == NULL) {
563                         DEBUG(0, ("malloc failed for referral\n"));
564                         goto out;
565                 }
566
567                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
568                 if (dp.reqpath[0] != '\0') {
569                         pstrcat(ref->alternate_path, dp.reqpath);
570                 }
571                 ref->proximity = 0;
572                 ref->ttl = REFERRAL_TTL;
573                 jucn->referral_list = ref;
574                 if (consumedcntp) {
575                         *consumedcntp = strlen(pathname);
576                 }
577                 ret = True;
578                 goto out;
579         }
580
581         pstrcpy(conn_path, lp_pathname(snum));
582         if (!create_conn_struct(conn, snum, conn_path)) {
583                 return False;
584         }
585
586         /* If not remote & not a self referral, return False */
587         if (!resolve_dfs_path(ctx, pathname, &dp, conn, False, 
588                               &jucn->referral_list, &jucn->referral_count,
589                               self_referralp, consumedcntp)) {
590                 if (!*self_referralp) {
591                         DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
592                         goto out;
593                 }
594         }
595         
596         /* if self_referral, fill up the junction map */
597         if (*self_referralp) {
598                 if (self_ref(ctx, pathname, jucn, consumedcntp, self_referralp) == False) {
599                         goto out;
600                 }
601         }
602         
603         ret = True;
604
605 out:
606
607         conn_free_internal(conn);
608         return ret;
609 }
610
611 static int setup_ver2_dfs_referral(char *pathname, char **ppdata, 
612                                    struct junction_map *junction,
613                                    int consumedcnt,
614                                    BOOL self_referral)
615 {
616         char* pdata = *ppdata;
617
618         unsigned char uni_requestedpath[1024];
619         int uni_reqpathoffset1,uni_reqpathoffset2;
620         int uni_curroffset;
621         int requestedpathlen=0;
622         int offset;
623         int reply_size = 0;
624         int i=0;
625
626         DEBUG(10,("setting up version2 referral\nRequested path:\n"));
627
628         requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring),
629                                        STR_TERMINATE);
630
631         if (DEBUGLVL(10)) {
632             dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
633         }
634
635         DEBUG(10,("ref count = %u\n",junction->referral_count));
636
637         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
638                         VERSION2_REFERRAL_SIZE * junction->referral_count;
639
640         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
641
642         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
643
644         reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
645                                         2 * requestedpathlen;
646         DEBUG(10,("reply_size: %u\n",reply_size));
647
648         /* add up the unicode lengths of all the referral paths */
649         for(i=0;i<junction->referral_count;i++) {
650                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
651                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
652         }
653
654         DEBUG(10,("reply_size = %u\n",reply_size));
655         /* add the unexplained 0x16 bytes */
656         reply_size += 0x16;
657
658         pdata = (char *)SMB_REALLOC(pdata,reply_size);
659         if(pdata == NULL) {
660                 DEBUG(0,("malloc failed for Realloc!\n"));
661                 return -1;
662         }
663         *ppdata = pdata;
664
665         /* copy in the dfs requested paths.. required for offset calculations */
666         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
667         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
668
669         /* create the header */
670         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
671         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
672         if(self_referral) {
673                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
674         } else {
675                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
676         }
677
678         offset = 8;
679         /* add the referral elements */
680         for(i=0;i<junction->referral_count;i++) {
681                 struct referral* ref = &junction->referral_list[i];
682                 int unilen;
683
684                 SSVAL(pdata,offset,2); /* version 2 */
685                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
686                 if(self_referral) {
687                         SSVAL(pdata,offset+4,1);
688                 } else {
689                         SSVAL(pdata,offset+4,0);
690                 }
691                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
692                 SIVAL(pdata,offset+8,ref->proximity);
693                 SIVAL(pdata,offset+12,ref->ttl);
694
695                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
696                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
697                 /* copy referred path into current offset */
698                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
699                                      sizeof(pstring), STR_UNICODE);
700
701                 SSVAL(pdata,offset+20,uni_curroffset-offset);
702
703                 uni_curroffset += unilen;
704                 offset += VERSION2_REFERRAL_SIZE;
705         }
706         /* add in the unexplained 22 (0x16) bytes at the end */
707         memset(pdata+uni_curroffset,'\0',0x16);
708         return reply_size;
709 }
710
711 static int setup_ver3_dfs_referral(char *pathname, char **ppdata, 
712                                    struct junction_map *junction,
713                                    int consumedcnt,
714                                    BOOL self_referral)
715 {
716         char* pdata = *ppdata;
717
718         unsigned char uni_reqpath[1024];
719         int uni_reqpathoffset1, uni_reqpathoffset2;
720         int uni_curroffset;
721         int reply_size = 0;
722
723         int reqpathlen = 0;
724         int offset,i=0;
725         
726         DEBUG(10,("setting up version3 referral\n"));
727
728         reqpathlen = rpcstr_push(uni_reqpath, pathname, sizeof(pstring), STR_TERMINATE);
729         
730         if (DEBUGLVL(10)) {
731             dump_data(0, (char *) uni_reqpath,reqpathlen);
732         }
733
734         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
735         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
736         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
737
738         for(i=0;i<junction->referral_count;i++) {
739                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
740                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
741         }
742
743         pdata = (char *)SMB_REALLOC(pdata,reply_size);
744         if(pdata == NULL) {
745                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
746                 return -1;
747         }
748         *ppdata = pdata;
749
750         /* create the header */
751         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
752         SSVAL(pdata,2,junction->referral_count); /* number of referral */
753         if(self_referral) {
754                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
755         } else {
756                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
757         }
758         
759         /* copy in the reqpaths */
760         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
761         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
762         
763         offset = 8;
764         for(i=0;i<junction->referral_count;i++) {
765                 struct referral* ref = &(junction->referral_list[i]);
766                 int unilen;
767
768                 SSVAL(pdata,offset,3); /* version 3 */
769                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
770                 if(self_referral) {
771                         SSVAL(pdata,offset+4,1);
772                 } else {
773                         SSVAL(pdata,offset+4,0);
774                 }
775
776                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
777                 SIVAL(pdata,offset+8,ref->ttl);
778             
779                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
780                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
781                 /* copy referred path into current offset */
782                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
783                                      sizeof(pstring), STR_UNICODE | STR_TERMINATE);
784                 SSVAL(pdata,offset+16,uni_curroffset-offset);
785                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
786                 memset(pdata+offset+18,'\0',16);
787
788                 uni_curroffset += unilen;
789                 offset += VERSION3_REFERRAL_SIZE;
790         }
791         return reply_size;
792 }
793
794 /******************************************************************
795  Set up the Dfs referral for the dfs pathname
796 ******************************************************************/
797
798 int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char **ppdata)
799 {
800         struct junction_map junction;
801         int consumedcnt;
802         BOOL self_referral = False;
803         pstring buf;
804         int reply_size = 0;
805         char *pathnamep = pathname;
806         TALLOC_CTX *ctx;
807
808         if (!(ctx=talloc_init("setup_dfs_referral"))) {
809                 return -1;
810         }
811
812         ZERO_STRUCT(junction);
813
814         /* get the junction entry */
815         if (!pathnamep) {
816                 talloc_destroy(ctx);
817                 return -1;
818         }
819
820         /* Trim pathname sent by client so it begins with only one backslash.
821            Two backslashes confuse some dfs clients
822          */
823         while (pathnamep[0] == '\\' && pathnamep[1] == '\\') {
824                 pathnamep++;
825         }
826
827         pstrcpy(buf, pathnamep);
828         /* The following call can change cwd. */
829         if (!get_referred_path(ctx, buf, &junction, &consumedcnt, &self_referral)) {
830                 vfs_ChDir(orig_conn,orig_conn->connectpath);
831                 talloc_destroy(ctx);
832                 return -1;
833         }
834         vfs_ChDir(orig_conn,orig_conn->connectpath);
835         
836         if (!self_referral) {
837                 pathnamep[consumedcnt] = '\0';
838
839                 if( DEBUGLVL( 3 ) ) {
840                         int i=0;
841                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
842                         for(i=0;i<junction.referral_count;i++)
843                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
844                         dbgtext(".\n");
845                 }
846         }
847
848         /* create the referral depeding on version */
849         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
850         if(max_referral_level<2 || max_referral_level>3) {
851                 max_referral_level = 2;
852         }
853
854         switch(max_referral_level) {
855         case 2:
856                 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
857                                                      consumedcnt, self_referral);
858                 break;
859         case 3:
860                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
861                                                      consumedcnt, self_referral);
862                 break;
863         default:
864                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
865                 talloc_destroy(ctx);
866                 return -1;
867         }
868       
869         if (DEBUGLVL(10)) {
870                 DEBUGADD(0,("DFS Referral pdata:\n"));
871                 dump_data(0,*ppdata,reply_size);
872         }
873
874         talloc_destroy(ctx);
875         return reply_size;
876 }
877
878 /**********************************************************************
879  The following functions are called by the NETDFS RPC pipe functions
880  **********************************************************************/
881
882 /*********************************************************************
883  Creates a junction structure from a Dfs pathname
884 **********************************************************************/
885
886 BOOL create_junction(const char *pathname, struct junction_map *jucn)
887 {
888         struct dfs_path dp;
889  
890         parse_dfs_path(pathname,&dp);
891
892         /* check if path is dfs : validate first token */
893         if (!is_myname_or_ipaddr(dp.hostname)) {
894                 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
895                         dp.hostname, pathname));
896                 return False;
897         }
898
899         /* Check for a non-DFS share */
900         if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
901                 DEBUG(4,("create_junction: %s is not an msdfs root.\n", dp.servicename));
902                 return False;
903         }
904
905         pstrcpy(jucn->service_name,dp.servicename);
906         pstrcpy(jucn->volume_name,dp.reqpath);
907         pstrcpy(jucn->comment, lp_comment(lp_servicenumber(dp.servicename)));
908         return True;
909 }
910
911 /**********************************************************************
912  Forms a valid Unix pathname from the junction 
913  **********************************************************************/
914
915 static BOOL junction_to_local_path(struct junction_map *jucn, char *path,
916                                    int max_pathlen, connection_struct *conn)
917 {
918         int snum;
919         pstring conn_path;
920
921         if(!path || !jucn) {
922                 return False;
923         }
924
925         snum = lp_servicenumber(jucn->service_name);
926         if(snum < 0) {
927                 return False;
928         }
929
930         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
931         safe_strcat(path, "/", max_pathlen-1);
932         safe_strcat(path, jucn->volume_name, max_pathlen-1);
933
934         pstrcpy(conn_path, lp_pathname(snum));
935         if (!create_conn_struct(conn, snum, conn_path)) {
936                 return False;
937         }
938
939         return True;
940 }
941
942 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
943 {
944         pstring path;
945         pstring msdfs_link;
946         connection_struct conns;
947         connection_struct *conn = &conns;
948         int i=0;
949         BOOL insert_comma = False;
950         BOOL ret = False;
951
952         ZERO_STRUCT(conns);
953
954         if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
955                 return False;
956         }
957   
958         /* form the msdfs_link contents */
959         pstrcpy(msdfs_link, "msdfs:");
960         for(i=0; i<jucn->referral_count; i++) {
961                 char* refpath = jucn->referral_list[i].alternate_path;
962       
963                 trim_char(refpath, '\\', '\\');
964                 if(*refpath == '\0') {
965                         if (i == 0) {
966                                 insert_comma = False;
967                         }
968                         continue;
969                 }
970                 if (i > 0 && insert_comma) {
971                         pstrcat(msdfs_link, ",");
972                 }
973
974                 pstrcat(msdfs_link, refpath);
975                 if (!insert_comma) {
976                         insert_comma = True;
977                 }
978         }
979
980         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
981
982         if(exists) {
983                 if(SMB_VFS_UNLINK(conn,path)!=0) {
984                         goto out;
985                 }
986         }
987
988         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
989                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
990                                 path, msdfs_link, strerror(errno)));
991                 goto out;
992         }
993         
994         
995         ret = True;
996         
997 out:
998
999         conn_free_internal(conn);
1000         return ret;
1001 }
1002
1003 BOOL remove_msdfs_link(struct junction_map *jucn)
1004 {
1005         pstring path;
1006         connection_struct conns;
1007         connection_struct *conn = &conns;
1008         BOOL ret = False;
1009
1010         ZERO_STRUCT(conns);
1011
1012         if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
1013                 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1014                         ret = True;
1015                 }
1016                 talloc_destroy( conn->mem_ctx );
1017         }
1018
1019         conn_free_internal(conn);
1020         return ret;
1021 }
1022
1023 static int form_junctions(TALLOC_CTX *ctx, int snum, struct junction_map *jucn, int jn_remain)
1024 {
1025         int cnt = 0;
1026         SMB_STRUCT_DIR *dirp;
1027         char* dname;
1028         pstring connect_path;
1029         char* service_name = lp_servicename(snum);
1030         connection_struct conn;
1031         struct referral *ref = NULL;
1032  
1033         ZERO_STRUCT(conn);
1034
1035         if (jn_remain <= 0) {
1036                 return 0;
1037         }
1038
1039         pstrcpy(connect_path,lp_pathname(snum));
1040
1041         if(*connect_path == '\0') {
1042                 return 0;
1043         }
1044
1045         /*
1046          * Fake up a connection struct for the VFS layer.
1047          */
1048
1049         if (!create_conn_struct(&conn, snum, connect_path)) {
1050                 return 0;
1051         }
1052
1053         /* form a junction for the msdfs root - convention 
1054            DO NOT REMOVE THIS: NT clients will not work with us
1055            if this is not present
1056         */ 
1057         pstrcpy(jucn[cnt].service_name, service_name);
1058         jucn[cnt].volume_name[0] = '\0';
1059         jucn[cnt].referral_count = 1;
1060
1061         ref = jucn[cnt].referral_list = TALLOC_P(ctx, struct referral);
1062         if (jucn[cnt].referral_list == NULL) {
1063                 DEBUG(0, ("Malloc failed!\n"));
1064                 goto out;
1065         }
1066
1067         ref->proximity = 0;
1068         ref->ttl = REFERRAL_TTL;
1069         if (*lp_msdfs_proxy(snum) != '\0') {
1070                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1071                 cnt++;
1072                 goto out;
1073         }
1074                 
1075         slprintf(ref->alternate_path, sizeof(pstring)-1,
1076                  "\\\\%s\\%s", get_local_machine_name(), service_name);
1077         cnt++;
1078
1079         /* Now enumerate all dfs links */
1080         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1081         if(!dirp) {
1082                 goto out;
1083         }
1084
1085         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1086                 if (cnt >= jn_remain) {
1087                         SMB_VFS_CLOSEDIR(&conn,dirp);
1088                         DEBUG(2, ("ran out of MSDFS junction slots"));
1089                         goto out;
1090                 }
1091                 if (is_msdfs_link(ctx, &conn, dname, &jucn[cnt].referral_list,
1092                                   &jucn[cnt].referral_count, NULL)) {
1093                         pstrcpy(jucn[cnt].service_name, service_name);
1094                         pstrcpy(jucn[cnt].volume_name, dname);
1095                         cnt++;
1096                 }
1097         }
1098         
1099         SMB_VFS_CLOSEDIR(&conn,dirp);
1100
1101 out:
1102
1103         conn_free_internal(&conn);
1104         return cnt;
1105 }
1106
1107 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1108 {
1109         int i=0;
1110         int sharecount = 0;
1111         int jn_count = 0;
1112
1113         if(!lp_host_msdfs()) {
1114                 return 0;
1115         }
1116
1117         /* Ensure all the usershares are loaded. */
1118         become_root();
1119         load_registry_shares();
1120         sharecount = load_usershare_shares();
1121         unbecome_root();
1122
1123         for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1124                 if(lp_msdfs_root(i)) {
1125                         jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1126                 }
1127         }
1128         return jn_count;
1129 }