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