trying to get HEAD building again. If you want the code
[tprouty/samba.git] / source3 / 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_m(path);
168
169         if (sbufp == NULL)
170                 sbufp = &st;
171
172         if (SMB_VFS_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 = SMB_VFS_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         int consumed_level = 1;
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         /* redirect if any component in the path is a link */
251         fstrcpy(reqpath, dp->reqpath);
252         p = strrchr(reqpath, '/');
253         while (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                                 for (; consumed_level; consumed_level--) {
272                                         q = strrchr(buf, '\\');
273                                         if (q) *q = 0;
274                                 }
275                                 *consumedcntp = strlen(buf);
276                                 DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
277                         }
278                         
279                         return True;
280                 }
281                 p = strrchr(reqpath, '/');
282                 consumed_level++;
283         }
284         
285         return False;
286 }
287
288 /*****************************************************************
289   Decides if a dfs pathname should be redirected or not.
290   If not, the pathname is converted to a tcon-relative local unix path
291 *****************************************************************/
292 BOOL dfs_redirect(char* pathname, connection_struct* conn,
293                   BOOL findfirst_flag)
294 {
295         struct dfs_path dp;
296         
297         if (!conn || !pathname)
298                 return False;
299
300         parse_dfs_path(pathname, &dp);
301
302         /* if dfs pathname for a non-dfs share, convert to tcon-relative
303            path and return false */
304         if (!lp_msdfs_root(SNUM(conn))) {
305                 fstrcpy(pathname, dp.reqpath);
306                 return False;
307         }
308         
309         if (strcasecmp(dp.servicename, lp_servicename(SNUM(conn)) ) != 0) 
310                 return False;
311
312         if (resolve_dfs_path(pathname, &dp, conn, findfirst_flag,
313                              NULL, NULL, NULL, NULL)) {
314                 DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
315                 return True;
316         } else {
317                 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
318                 
319                 /* Form non-dfs tcon-relative path */
320                 fstrcpy(pathname, dp.reqpath);
321                 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n",
322                          pathname));
323                 return False;
324         }
325         /* never reached */
326         return False;
327 }
328
329 /**********************************************************************
330  Gets valid referrals for a dfs path and fills up the
331  junction_map structure
332  **********************************************************************/
333 BOOL get_referred_path(char *pathname, struct junction_map* jn,
334                        int* consumedcntp, BOOL* self_referralp)
335 {
336         struct dfs_path dp;
337
338         struct connection_struct conns;
339         struct connection_struct* conn = &conns;
340         pstring conn_path;
341         int snum;
342
343         BOOL self_referral = False;
344
345         if (!pathname || !jn)
346                 return False;
347
348         if (self_referralp)
349                 *self_referralp = False;
350         else
351                 self_referralp = &self_referral;
352
353         parse_dfs_path(pathname, &dp);
354
355         /* Verify hostname in path */
356         if (local_machine && (strcasecmp(local_machine, dp.hostname) != 0)) {
357
358            /* Hostname mismatch, check if one of our IP addresses */
359            if (!ismyip(*interpret_addr2(dp.hostname))) {
360                 
361                 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
362                           dp.hostname, pathname));
363                 return False;
364            }
365         }
366
367         pstrcpy(jn->service_name, dp.servicename);
368         pstrcpy(jn->volume_name, dp.reqpath);
369
370         /* Verify the share is a dfs root */
371         snum = lp_servicenumber(jn->service_name);
372         if(snum < 0) {
373                 if ((snum = find_service(jn->service_name)) < 0)
374                         return False;
375         }
376         
377         pstrcpy(conn_path, lp_pathname(snum));
378         if (!create_conn_struct(conn, snum, conn_path))
379                 return False;
380         
381         if (!lp_msdfs_root(SNUM(conn))) {
382                 DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
383                          dp.servicename, pathname));
384                 return False;
385         }
386
387         if (*lp_msdfs_proxy(snum) != '\0') {
388                 struct referral* ref;
389                 jn->referral_count = 1;
390                 if ((ref = (struct referral*) malloc(sizeof(struct referral)))
391                     == NULL) {
392                         DEBUG(0, ("malloc failed for referral\n"));
393                         return False;
394                 }
395
396                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
397                 if (dp.reqpath[0] != '\0')
398                         pstrcat(ref->alternate_path, dp.reqpath);
399                 ref->proximity = 0;
400                 ref->ttl = REFERRAL_TTL;
401                 jn->referral_list = ref;
402                 if (consumedcntp)
403                         *consumedcntp = strlen(pathname);
404                 return True;
405         }
406
407         /* If not remote & not a self referral, return False */
408         if (!resolve_dfs_path(pathname, &dp, conn, False, 
409                               &jn->referral_list, &jn->referral_count,
410                               self_referralp, consumedcntp)) {
411                 if (!*self_referralp) {
412                         DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
413                         return False;
414                 }
415         }
416         
417         /* if self_referral, fill up the junction map */
418         if (*self_referralp) {
419                 struct referral* ref;
420                 jn->referral_count = 1;
421                 if((ref = (struct referral*) malloc(sizeof(struct referral)))
422                    == NULL) {
423                         DEBUG(0,("malloc failed for referral\n"));
424                         return False;
425                 }
426       
427                 pstrcpy(ref->alternate_path,pathname);
428                 ref->proximity = 0;
429                 ref->ttl = REFERRAL_TTL;
430                 jn->referral_list = ref;
431                 if (consumedcntp)
432                         *consumedcntp = strlen(pathname);
433         }
434
435         return True;
436 }
437
438 static int setup_ver2_dfs_referral(char* pathname, char** ppdata, 
439                                    struct junction_map* junction,
440                                    int consumedcnt,
441                                    BOOL self_referral)
442 {
443         char* pdata = *ppdata;
444
445         unsigned char uni_requestedpath[1024];
446         int uni_reqpathoffset1,uni_reqpathoffset2;
447         int uni_curroffset;
448         int requestedpathlen=0;
449         int offset;
450         int reply_size = 0;
451         int i=0;
452
453         DEBUG(10,("setting up version2 referral\nRequested path:\n"));
454
455         requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
456                                        STR_TERMINATE);
457
458         dump_data(10, (const char *) uni_requestedpath,requestedpathlen);
459
460         DEBUG(10,("ref count = %u\n",junction->referral_count));
461
462         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
463                         VERSION2_REFERRAL_SIZE * junction->referral_count;
464
465         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
466
467         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
468
469         reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
470                                         2 * requestedpathlen;
471         DEBUG(10,("reply_size: %u\n",reply_size));
472
473         /* add up the unicode lengths of all the referral paths */
474         for(i=0;i<junction->referral_count;i++) {
475                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
476                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
477         }
478
479         DEBUG(10,("reply_size = %u\n",reply_size));
480         /* add the unexplained 0x16 bytes */
481         reply_size += 0x16;
482
483         pdata = Realloc(pdata,reply_size);
484         if(pdata == NULL) {
485                 DEBUG(0,("malloc failed for Realloc!\n"));
486                 return -1;
487         } else
488                 *ppdata = pdata;
489
490         /* copy in the dfs requested paths.. required for offset calculations */
491         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
492         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
493
494         /* create the header */
495         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
496         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
497         if(self_referral)
498                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
499         else
500                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
501
502         offset = 8;
503         /* add the referral elements */
504         for(i=0;i<junction->referral_count;i++) {
505                 struct referral* ref = &(junction->referral_list[i]);
506                 int unilen;
507
508                 SSVAL(pdata,offset,2); /* version 2 */
509                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
510                 if(self_referral)
511                         SSVAL(pdata,offset+4,1);
512                 else
513                         SSVAL(pdata,offset+4,0);
514                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
515                 SIVAL(pdata,offset+8,ref->proximity);
516                 SIVAL(pdata,offset+12,ref->ttl);
517
518                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
519                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
520                 /* copy referred path into current offset */
521                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
522                                      -1, STR_UNICODE);
523
524                 SSVAL(pdata,offset+20,uni_curroffset-offset);
525
526                 uni_curroffset += unilen;
527                 offset += VERSION2_REFERRAL_SIZE;
528         }
529         /* add in the unexplained 22 (0x16) bytes at the end */
530         memset(pdata+uni_curroffset,'\0',0x16);
531         return reply_size;
532 }
533
534 static int setup_ver3_dfs_referral(char* pathname, char** ppdata, 
535                                    struct junction_map* junction,
536                                    int consumedcnt,
537                                    BOOL self_referral)
538 {
539         char* pdata = *ppdata;
540
541         unsigned char uni_reqpath[1024];
542         int uni_reqpathoffset1, uni_reqpathoffset2;
543         int uni_curroffset;
544         int reply_size = 0;
545
546         int reqpathlen = 0;
547         int offset,i=0;
548         
549         DEBUG(10,("setting up version3 referral\n"));
550
551         reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
552         
553         dump_data(10, (char *) uni_reqpath,reqpathlen);
554
555         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
556         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
557         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
558
559         for(i=0;i<junction->referral_count;i++) {
560                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
561                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
562         }
563
564         pdata = Realloc(pdata,reply_size);
565         if(pdata == NULL) {
566                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
567                 return -1;
568         } else
569                 *ppdata = pdata;
570
571         /* create the header */
572         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
573         SSVAL(pdata,2,junction->referral_count); /* number of referral */
574         if(self_referral)
575                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
576         else
577                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
578         
579         /* copy in the reqpaths */
580         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
581         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
582         
583         offset = 8;
584         for(i=0;i<junction->referral_count;i++) {
585                 struct referral* ref = &(junction->referral_list[i]);
586                 int unilen;
587
588                 SSVAL(pdata,offset,3); /* version 3 */
589                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
590                 if(self_referral)
591                         SSVAL(pdata,offset+4,1);
592                 else
593                         SSVAL(pdata,offset+4,0);
594
595                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
596                 SIVAL(pdata,offset+8,ref->ttl);
597             
598                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
599                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
600                 /* copy referred path into current offset */
601                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
602                                      -1, STR_UNICODE | STR_TERMINATE);
603                 SSVAL(pdata,offset+16,uni_curroffset-offset);
604                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
605                 memset(pdata+offset+18,'\0',16);
606
607                 uni_curroffset += unilen;
608                 offset += VERSION3_REFERRAL_SIZE;
609         }
610         return reply_size;
611 }
612
613 /******************************************************************
614  * Set up the Dfs referral for the dfs pathname
615  ******************************************************************/
616
617 int setup_dfs_referral(char* pathname, int max_referral_level, char** ppdata)
618 {
619         struct junction_map junction;
620         int consumedcnt;
621         BOOL self_referral = False;
622         pstring buf;
623         int reply_size = 0;
624         char *pathnamep = pathname;
625
626         ZERO_STRUCT(junction);
627
628         /* get the junction entry */
629         if (!pathnamep)
630                 return -1;
631
632         /* Trim pathname sent by client so it begins with only one backslash.
633            Two backslashes confuse some dfs clients
634          */
635         while (pathnamep[0] == '\\' && pathnamep[1] == '\\')
636                 pathnamep++;
637
638         pstrcpy(buf, pathnamep);
639         if (!get_referred_path(buf, &junction, &consumedcnt,
640                                &self_referral))
641                 return -1;
642         
643         if (!self_referral)
644         {
645                 pathnamep[consumedcnt] = '\0';
646
647                 if( DEBUGLVL( 3 ) ) {
648                         int i=0;
649                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
650                         for(i=0;i<junction.referral_count;i++)
651                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
652                         dbgtext(".\n");
653                 }
654         }
655
656         /* create the referral depeding on version */
657         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
658         if(max_referral_level<2 || max_referral_level>3)
659                 max_referral_level = 2;
660
661         switch(max_referral_level) {
662         case 2:
663                 {
664                 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
665                                                      consumedcnt, self_referral);
666                 SAFE_FREE(junction.referral_list);
667                 break;
668                 }
669         case 3:
670                 {
671                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
672                                                      consumedcnt, self_referral);
673                 SAFE_FREE(junction.referral_list);
674                 break;
675                 }
676         default:
677                 {
678                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
679                 return -1;
680                 }
681         }
682       
683         DEBUG(10,("DFS Referral pdata:\n"));
684         dump_data(10,*ppdata,reply_size);
685         return reply_size;
686 }
687
688 /**********************************************************************
689  The following functions are called by the NETDFS RPC pipe functions
690  **********************************************************************/
691
692 /**********************************************************************
693  Creates a junction structure from a Dfs pathname
694  **********************************************************************/
695 BOOL create_junction(char* pathname, struct junction_map* jn)
696 {
697         struct dfs_path dp;
698  
699         parse_dfs_path(pathname,&dp);
700
701         /* check if path is dfs : validate first token */
702         if (local_machine && (strcasecmp(local_machine,dp.hostname)!=0)) {
703             
704            /* Hostname mismatch, check if one of our IP addresses */
705            if (!ismyip(*interpret_addr2(dp.hostname))) {
706                 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
707                          dp.hostname, pathname));
708                 return False;
709            }
710         }
711
712         /* Check for a non-DFS share */
713         if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
714                 DEBUG(4,("create_junction: %s is not an msdfs root.\n", 
715                          dp.servicename));
716                 return False;
717         }
718
719         pstrcpy(jn->service_name,dp.servicename);
720         pstrcpy(jn->volume_name,dp.reqpath);
721         return True;
722 }
723
724 /**********************************************************************
725  Forms a valid Unix pathname from the junction 
726  **********************************************************************/
727
728 static BOOL junction_to_local_path(struct junction_map* jn, char* path,
729                                    int max_pathlen, connection_struct *conn)
730 {
731         int snum;
732         pstring conn_path;
733
734         if(!path || !jn)
735                 return False;
736
737         snum = lp_servicenumber(jn->service_name);
738         if(snum < 0)
739                 return False;
740
741         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
742         safe_strcat(path, "/", max_pathlen-1);
743         strlower_m(jn->volume_name);
744         safe_strcat(path, jn->volume_name, max_pathlen-1);
745
746         pstrcpy(conn_path, lp_pathname(snum));
747         if (!create_conn_struct(conn, snum, conn_path))
748                 return False;
749
750         return True;
751 }
752
753 BOOL create_msdfs_link(struct junction_map* jn, BOOL exists)
754 {
755         pstring path;
756         pstring msdfs_link;
757         connection_struct conns;
758         connection_struct *conn = &conns;
759         int i=0;
760         BOOL insert_comma = False;
761
762         if(!junction_to_local_path(jn, path, sizeof(path), conn))
763                 return False;
764   
765         /* form the msdfs_link contents */
766         pstrcpy(msdfs_link, "msdfs:");
767         for(i=0; i<jn->referral_count; i++) {
768                 char* refpath = jn->referral_list[i].alternate_path;
769       
770                 trim_string(refpath, "\\", "\\");
771                 if(*refpath == '\0') {
772                         if (i == 0)
773                                 insert_comma = False;
774                         continue;
775                 }
776                 if (i > 0 && insert_comma)
777                         pstrcat(msdfs_link, ",");
778
779                 pstrcat(msdfs_link, refpath);
780                 if (!insert_comma)
781                         insert_comma = True;
782                 
783         }
784
785         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
786
787         if(exists)
788                 if(SMB_VFS_UNLINK(conn,path)!=0)
789                         return False;
790
791         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
792                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
793                                 path, msdfs_link, strerror(errno)));
794                 return False;
795         }
796         return True;
797 }
798
799 BOOL remove_msdfs_link(struct junction_map* jn)
800 {
801         pstring path;
802         connection_struct conns;
803         connection_struct *conn = &conns;
804
805         if(!junction_to_local_path(jn, path, sizeof(path), conn))
806                 return False;
807      
808         if(SMB_VFS_UNLINK(conn, path)!=0)
809                 return False;
810   
811         return True;
812 }
813
814 static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count)
815 {
816         int cnt = *jn_count;
817         DIR *dirp;
818         char* dname;
819         pstring connect_path;
820         char* service_name = lp_servicename(snum);
821         connection_struct conns;
822         connection_struct *conn = &conns;
823         struct referral *ref = NULL;
824  
825         pstrcpy(connect_path,lp_pathname(snum));
826
827         if(*connect_path == '\0')
828                 return False;
829
830         /*
831          * Fake up a connection struct for the VFS layer.
832          */
833
834         if (!create_conn_struct(conn, snum, connect_path))
835                 return False;
836
837         /* form a junction for the msdfs root - convention 
838            DO NOT REMOVE THIS: NT clients will not work with us
839            if this is not present
840         */ 
841         pstrcpy(jn[cnt].service_name, service_name);
842         jn[cnt].volume_name[0] = '\0';
843         jn[cnt].referral_count = 1;
844
845         ref = jn[cnt].referral_list
846                 = (struct referral*) malloc(sizeof(struct referral));
847         if (jn[cnt].referral_list == NULL) {
848                 DEBUG(0, ("Malloc failed!\n"));
849                 return False;
850         }
851
852         ref->proximity = 0;
853         ref->ttl = REFERRAL_TTL;
854         if (*lp_msdfs_proxy(snum) != '\0') {
855                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
856                 *jn_count = ++cnt;
857                 return True;
858         }
859                 
860         slprintf(ref->alternate_path, sizeof(pstring)-1,
861                  "\\\\%s\\%s", local_machine, service_name);
862         cnt++;
863         
864         /* Now enumerate all dfs links */
865         dirp = SMB_VFS_OPENDIR(conn, connect_path);
866         if(!dirp)
867                 return False;
868
869         while((dname = vfs_readdirname(conn, dirp)) != NULL) {
870                 pstring pathreal;
871
872                 pstrcpy(pathreal, connect_path);
873                 pstrcat(pathreal, "/");
874                 pstrcat(pathreal, dname);
875  
876                 if (is_msdfs_link(conn, pathreal, &(jn[cnt].referral_list),
877                                   &(jn[cnt].referral_count), NULL)) {
878                         pstrcpy(jn[cnt].service_name, service_name);
879                         pstrcpy(jn[cnt].volume_name, dname);
880                         cnt++;
881                 }
882         }
883         
884         SMB_VFS_CLOSEDIR(conn,dirp);
885         *jn_count = cnt;
886         return True;
887 }
888
889 int enum_msdfs_links(struct junction_map* jn)
890 {
891         int i=0;
892         int jn_count = 0;
893
894         if(!lp_host_msdfs())
895                 return -1;
896
897         for(i=0;*lp_servicename(i);i++) {
898                 if(lp_msdfs_root(i)) 
899                         form_junctions(i,jn,&jn_count);
900         }
901         return jn_count;
902 }
903