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