r4088: Get medieval on our ass about malloc.... :-). Take control of all our allocation
[tprouty/samba.git] / source / smbd / msdfs.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    MSDfs services for Samba
5    Copyright (C) Shirish Kalele 2000
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 */
22
23 #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,True);
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,True);
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 = SMB_MALLOC_ARRAY(struct referral, count);
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 = SMB_MALLOC_P(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
499                 struct referral* ref;
500
501                 if (*lp_msdfs_proxy(snum) == '\0')
502                         return self_ref(pathname, jucn, consumedcntp,
503                                         self_referralp);
504
505                 jucn->referral_count = 1;
506                 if ((ref = SMB_MALLOC_P(struct referral)) == NULL) {
507                         DEBUG(0, ("malloc failed for referral\n"));
508                         goto out;
509                 }
510
511                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
512                 if (dp.reqpath[0] != '\0')
513                         pstrcat(ref->alternate_path, dp.reqpath);
514                 ref->proximity = 0;
515                 ref->ttl = REFERRAL_TTL;
516                 jucn->referral_list = ref;
517                 if (consumedcntp)
518                         *consumedcntp = strlen(pathname);
519                 ret = True;
520                 goto out;
521         }
522
523         pstrcpy(conn_path, lp_pathname(snum));
524         if (!create_conn_struct(conn, snum, conn_path))
525                 return False;
526
527         /* If not remote & not a self referral, return False */
528         if (!resolve_dfs_path(pathname, &dp, conn, False, 
529                               &jucn->referral_list, &jucn->referral_count,
530                               self_referralp, consumedcntp)) {
531                 if (!*self_referralp) {
532                         DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
533                         goto out;
534                 }
535         }
536         
537         /* if self_referral, fill up the junction map */
538         if (*self_referralp) {
539                 if (self_ref(pathname, jucn, consumedcntp, self_referralp) == False) {
540                         goto out;
541                 }
542         }
543         
544         ret = True;
545
546 out:
547         if (conn->mem_ctx)
548                 talloc_destroy( conn->mem_ctx );
549         
550         return ret;
551 }
552
553 static int setup_ver2_dfs_referral(char* pathname, char** ppdata, 
554                                    struct junction_map* junction,
555                                    int consumedcnt,
556                                    BOOL self_referral)
557 {
558         char* pdata = *ppdata;
559
560         unsigned char uni_requestedpath[1024];
561         int uni_reqpathoffset1,uni_reqpathoffset2;
562         int uni_curroffset;
563         int requestedpathlen=0;
564         int offset;
565         int reply_size = 0;
566         int i=0;
567
568         DEBUG(10,("setting up version2 referral\nRequested path:\n"));
569
570         requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
571                                        STR_TERMINATE);
572
573         dump_data(10, (const char *) uni_requestedpath,requestedpathlen);
574
575         DEBUG(10,("ref count = %u\n",junction->referral_count));
576
577         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + 
578                         VERSION2_REFERRAL_SIZE * junction->referral_count;
579
580         uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
581
582         uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
583
584         reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
585                                         2 * requestedpathlen;
586         DEBUG(10,("reply_size: %u\n",reply_size));
587
588         /* add up the unicode lengths of all the referral paths */
589         for(i=0;i<junction->referral_count;i++) {
590                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
591                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
592         }
593
594         DEBUG(10,("reply_size = %u\n",reply_size));
595         /* add the unexplained 0x16 bytes */
596         reply_size += 0x16;
597
598         pdata = SMB_REALLOC(pdata,reply_size);
599         if(pdata == NULL) {
600                 DEBUG(0,("malloc failed for Realloc!\n"));
601                 return -1;
602         } else
603                 *ppdata = pdata;
604
605         /* copy in the dfs requested paths.. required for offset calculations */
606         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
607         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
608
609         /* create the header */
610         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
611         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
612         if(self_referral)
613                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
614         else
615                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
616
617         offset = 8;
618         /* add the referral elements */
619         for(i=0;i<junction->referral_count;i++) {
620                 struct referral* ref = &junction->referral_list[i];
621                 int unilen;
622
623                 SSVAL(pdata,offset,2); /* version 2 */
624                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
625                 if(self_referral)
626                         SSVAL(pdata,offset+4,1);
627                 else
628                         SSVAL(pdata,offset+4,0);
629                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
630                 SIVAL(pdata,offset+8,ref->proximity);
631                 SIVAL(pdata,offset+12,ref->ttl);
632
633                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
634                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
635                 /* copy referred path into current offset */
636                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
637                                      -1, STR_UNICODE);
638
639                 SSVAL(pdata,offset+20,uni_curroffset-offset);
640
641                 uni_curroffset += unilen;
642                 offset += VERSION2_REFERRAL_SIZE;
643         }
644         /* add in the unexplained 22 (0x16) bytes at the end */
645         memset(pdata+uni_curroffset,'\0',0x16);
646         return reply_size;
647 }
648
649 static int setup_ver3_dfs_referral(char* pathname, char** ppdata, 
650                                    struct junction_map* junction,
651                                    int consumedcnt,
652                                    BOOL self_referral)
653 {
654         char* pdata = *ppdata;
655
656         unsigned char uni_reqpath[1024];
657         int uni_reqpathoffset1, uni_reqpathoffset2;
658         int uni_curroffset;
659         int reply_size = 0;
660
661         int reqpathlen = 0;
662         int offset,i=0;
663         
664         DEBUG(10,("setting up version3 referral\n"));
665
666         reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
667         
668         dump_data(10, (char *) uni_reqpath,reqpathlen);
669
670         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
671         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
672         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
673
674         for(i=0;i<junction->referral_count;i++) {
675                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
676                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
677         }
678
679         pdata = SMB_REALLOC(pdata,reply_size);
680         if(pdata == NULL) {
681                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
682                 return -1;
683         } else
684                 *ppdata = pdata;
685
686         /* create the header */
687         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
688         SSVAL(pdata,2,junction->referral_count); /* number of referral */
689         if(self_referral)
690                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
691         else
692                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
693         
694         /* copy in the reqpaths */
695         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
696         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
697         
698         offset = 8;
699         for(i=0;i<junction->referral_count;i++) {
700                 struct referral* ref = &(junction->referral_list[i]);
701                 int unilen;
702
703                 SSVAL(pdata,offset,3); /* version 3 */
704                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
705                 if(self_referral)
706                         SSVAL(pdata,offset+4,1);
707                 else
708                         SSVAL(pdata,offset+4,0);
709
710                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
711                 SIVAL(pdata,offset+8,ref->ttl);
712             
713                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
714                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
715                 /* copy referred path into current offset */
716                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
717                                      -1, STR_UNICODE | STR_TERMINATE);
718                 SSVAL(pdata,offset+16,uni_curroffset-offset);
719                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
720                 memset(pdata+offset+18,'\0',16);
721
722                 uni_curroffset += unilen;
723                 offset += VERSION3_REFERRAL_SIZE;
724         }
725         return reply_size;
726 }
727
728 /******************************************************************
729  * Set up the Dfs referral for the dfs pathname
730  ******************************************************************/
731
732 int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char** ppdata)
733 {
734         struct junction_map junction;
735         int consumedcnt;
736         BOOL self_referral = False;
737         pstring buf;
738         int reply_size = 0;
739         char *pathnamep = pathname;
740
741         ZERO_STRUCT(junction);
742
743         /* get the junction entry */
744         if (!pathnamep)
745                 return -1;
746
747         /* Trim pathname sent by client so it begins with only one backslash.
748            Two backslashes confuse some dfs clients
749          */
750         while (pathnamep[0] == '\\' && pathnamep[1] == '\\')
751                 pathnamep++;
752
753         pstrcpy(buf, pathnamep);
754         /* The following call can change cwd. */
755         if (!get_referred_path(buf, &junction, &consumedcnt, &self_referral)) {
756                 vfs_ChDir(orig_conn,orig_conn->connectpath);
757                 return -1;
758         }
759         vfs_ChDir(orig_conn,orig_conn->connectpath);
760         
761         if (!self_referral) {
762                 pathnamep[consumedcnt] = '\0';
763
764                 if( DEBUGLVL( 3 ) ) {
765                         int i=0;
766                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
767                         for(i=0;i<junction.referral_count;i++)
768                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
769                         dbgtext(".\n");
770                 }
771         }
772
773         /* create the referral depeding on version */
774         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
775         if(max_referral_level<2 || max_referral_level>3)
776                 max_referral_level = 2;
777
778         switch(max_referral_level) {
779         case 2:
780                 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
781                                                      consumedcnt, self_referral);
782                 SAFE_FREE(junction.referral_list);
783                 break;
784         case 3:
785                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
786                                                      consumedcnt, self_referral);
787                 SAFE_FREE(junction.referral_list);
788                 break;
789         default:
790                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
791                 return -1;
792         }
793       
794         DEBUG(10,("DFS Referral pdata:\n"));
795         dump_data(10,*ppdata,reply_size);
796         return reply_size;
797 }
798
799 /**********************************************************************
800  The following functions are called by the NETDFS RPC pipe functions
801  **********************************************************************/
802
803 /**********************************************************************
804  Creates a junction structure from a Dfs pathname
805  **********************************************************************/
806 BOOL create_junction(char* pathname, struct junction_map* jucn)
807 {
808         struct dfs_path dp;
809  
810         parse_dfs_path(pathname,&dp);
811
812         /* check if path is dfs : validate first token */
813         if (local_machine && (!strequal(local_machine,dp.hostname))) {
814             
815            /* Hostname mismatch, check if one of our IP addresses */
816            if (!ismyip(*interpret_addr2(dp.hostname))) {
817                 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
818                          dp.hostname, pathname));
819                 return False;
820            }
821         }
822
823         /* Check for a non-DFS share */
824         if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
825                 DEBUG(4,("create_junction: %s is not an msdfs root.\n", 
826                          dp.servicename));
827                 return False;
828         }
829
830         pstrcpy(jucn->service_name,dp.servicename);
831         pstrcpy(jucn->volume_name,dp.reqpath);
832         return True;
833 }
834
835 /**********************************************************************
836  Forms a valid Unix pathname from the junction 
837  **********************************************************************/
838
839 static BOOL junction_to_local_path(struct junction_map* jucn, char* path,
840                                    int max_pathlen, connection_struct *conn)
841 {
842         int snum;
843         pstring conn_path;
844
845         if(!path || !jucn)
846                 return False;
847
848         snum = lp_servicenumber(jucn->service_name);
849         if(snum < 0)
850                 return False;
851
852         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
853         safe_strcat(path, "/", max_pathlen-1);
854         safe_strcat(path, jucn->volume_name, max_pathlen-1);
855
856         pstrcpy(conn_path, lp_pathname(snum));
857         if (!create_conn_struct(conn, snum, conn_path))
858                 return False;
859
860         return True;
861 }
862
863 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
864 {
865         pstring path;
866         pstring msdfs_link;
867         connection_struct conns;
868         connection_struct *conn = &conns;
869         int i=0;
870         BOOL insert_comma = False;
871         BOOL ret = False;
872
873         if(!junction_to_local_path(jucn, path, sizeof(path), conn))
874                 return False;
875   
876         /* form the msdfs_link contents */
877         pstrcpy(msdfs_link, "msdfs:");
878         for(i=0; i<jucn->referral_count; i++) {
879                 char* refpath = jucn->referral_list[i].alternate_path;
880       
881                 trim_char(refpath, '\\', '\\');
882                 if(*refpath == '\0') {
883                         if (i == 0)
884                                 insert_comma = False;
885                         continue;
886                 }
887                 if (i > 0 && insert_comma)
888                         pstrcat(msdfs_link, ",");
889
890                 pstrcat(msdfs_link, refpath);
891                 if (!insert_comma)
892                         insert_comma = True;
893                 
894         }
895
896         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
897
898         if(exists)
899                 if(SMB_VFS_UNLINK(conn,path)!=0)
900                         goto out;
901
902         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
903                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
904                                 path, msdfs_link, strerror(errno)));
905                 goto out;
906         }
907         
908         
909         ret = True;
910         
911 out:
912         talloc_destroy( conn->mem_ctx );
913         return ret;
914 }
915
916 BOOL remove_msdfs_link(struct junction_map* jucn)
917 {
918         pstring path;
919         connection_struct conns;
920         connection_struct *conn = &conns;
921         BOOL ret = False;
922
923         if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
924                 if( SMB_VFS_UNLINK(conn, path) == 0 )
925                         ret = True;
926
927                 talloc_destroy( conn->mem_ctx );
928         }
929         
930         return ret;
931 }
932
933 static BOOL form_junctions(int snum, struct junction_map* jucn, int* jn_count)
934 {
935         int cnt = *jn_count;
936         DIR *dirp;
937         char* dname;
938         pstring connect_path;
939         char* service_name = lp_servicename(snum);
940         connection_struct conns;
941         connection_struct *conn = &conns;
942         struct referral *ref = NULL;
943         BOOL ret = False;
944  
945         pstrcpy(connect_path,lp_pathname(snum));
946
947         if(*connect_path == '\0')
948                 return False;
949
950         /*
951          * Fake up a connection struct for the VFS layer.
952          */
953
954         if (!create_conn_struct(conn, snum, connect_path))
955                 return False;
956
957         /* form a junction for the msdfs root - convention 
958            DO NOT REMOVE THIS: NT clients will not work with us
959            if this is not present
960         */ 
961         pstrcpy(jucn[cnt].service_name, service_name);
962         jucn[cnt].volume_name[0] = '\0';
963         jucn[cnt].referral_count = 1;
964
965         ref = jucn[cnt].referral_list = SMB_MALLOC_P(struct referral);
966         if (jucn[cnt].referral_list == NULL) {
967                 DEBUG(0, ("Malloc failed!\n"));
968                 goto out;
969         }
970
971         ref->proximity = 0;
972         ref->ttl = REFERRAL_TTL;
973         if (*lp_msdfs_proxy(snum) != '\0') {
974                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
975                 *jn_count = ++cnt;
976                 ret = True;
977                 goto out;
978         }
979                 
980         slprintf(ref->alternate_path, sizeof(pstring)-1,
981                  "\\\\%s\\%s", local_machine, service_name);
982         cnt++;
983         
984         /* Now enumerate all dfs links */
985         dirp = SMB_VFS_OPENDIR(conn, ".");
986         if(!dirp)
987                 goto out;
988
989         while((dname = vfs_readdirname(conn, dirp)) != NULL) {
990                 if (is_msdfs_link(conn, dname, &(jucn[cnt].referral_list),
991                                   &(jucn[cnt].referral_count), NULL)) {
992                         pstrcpy(jucn[cnt].service_name, service_name);
993                         pstrcpy(jucn[cnt].volume_name, dname);
994                         cnt++;
995                 }
996         }
997         
998         SMB_VFS_CLOSEDIR(conn,dirp);
999         *jn_count = cnt;
1000 out:
1001         talloc_destroy(conn->mem_ctx);
1002         return ret;
1003 }
1004
1005 int enum_msdfs_links(struct junction_map* jucn)
1006 {
1007         int i=0;
1008         int jn_count = 0;
1009
1010         if(!lp_host_msdfs())
1011                 return 0;
1012
1013         for(i=0;i < lp_numservices();i++) {
1014                 if(lp_msdfs_root(i)) 
1015                         form_junctions(i,jucn,&jn_count);
1016         }
1017         return jn_count;
1018 }