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