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