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