r14336: Try and quieten coverity #53 and #54. Make it obvious
[tprouty/samba.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 #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         set_conn_connectpath(conn, 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         }
647         *ppdata = pdata;
648
649         /* copy in the dfs requested paths.. required for offset calculations */
650         memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
651         memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
652
653         /* create the header */
654         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
655         SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
656         if(self_referral) {
657                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
658         } else {
659                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
660         }
661
662         offset = 8;
663         /* add the referral elements */
664         for(i=0;i<junction->referral_count;i++) {
665                 struct referral* ref = &junction->referral_list[i];
666                 int unilen;
667
668                 SSVAL(pdata,offset,2); /* version 2 */
669                 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
670                 if(self_referral) {
671                         SSVAL(pdata,offset+4,1);
672                 } else {
673                         SSVAL(pdata,offset+4,0);
674                 }
675                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
676                 SIVAL(pdata,offset+8,ref->proximity);
677                 SIVAL(pdata,offset+12,ref->ttl);
678
679                 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
680                 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
681                 /* copy referred path into current offset */
682                 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
683                                      (size_t)-1, STR_UNICODE);
684
685                 SSVAL(pdata,offset+20,uni_curroffset-offset);
686
687                 uni_curroffset += unilen;
688                 offset += VERSION2_REFERRAL_SIZE;
689         }
690         /* add in the unexplained 22 (0x16) bytes at the end */
691         memset(pdata+uni_curroffset,'\0',0x16);
692         return reply_size;
693 }
694
695 static int setup_ver3_dfs_referral(char *pathname, char **ppdata, 
696                                    struct junction_map *junction,
697                                    int consumedcnt,
698                                    BOOL self_referral)
699 {
700         char* pdata = *ppdata;
701
702         unsigned char uni_reqpath[1024];
703         int uni_reqpathoffset1, uni_reqpathoffset2;
704         int uni_curroffset;
705         int reply_size = 0;
706
707         int reqpathlen = 0;
708         int offset,i=0;
709         
710         DEBUG(10,("setting up version3 referral\n"));
711
712         reqpathlen = rpcstr_push(uni_reqpath, pathname, (size_t)-1, STR_TERMINATE);
713         
714         if (DEBUGLVL(10)) {
715             dump_data(0, (char *) uni_reqpath,reqpathlen);
716         }
717
718         uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
719         uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
720         reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
721
722         for(i=0;i<junction->referral_count;i++) {
723                 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
724                 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
725         }
726
727         pdata = SMB_REALLOC(pdata,reply_size);
728         if(pdata == NULL) {
729                 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
730                 return -1;
731         }
732         *ppdata = pdata;
733
734         /* create the header */
735         SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
736         SSVAL(pdata,2,junction->referral_count); /* number of referral */
737         if(self_referral) {
738                 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER); 
739         } else {
740                 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
741         }
742         
743         /* copy in the reqpaths */
744         memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
745         memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
746         
747         offset = 8;
748         for(i=0;i<junction->referral_count;i++) {
749                 struct referral* ref = &(junction->referral_list[i]);
750                 int unilen;
751
752                 SSVAL(pdata,offset,3); /* version 3 */
753                 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
754                 if(self_referral) {
755                         SSVAL(pdata,offset+4,1);
756                 } else {
757                         SSVAL(pdata,offset+4,0);
758                 }
759
760                 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
761                 SIVAL(pdata,offset+8,ref->ttl);
762             
763                 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
764                 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
765                 /* copy referred path into current offset */
766                 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
767                                      -1, STR_UNICODE | STR_TERMINATE);
768                 SSVAL(pdata,offset+16,uni_curroffset-offset);
769                 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
770                 memset(pdata+offset+18,'\0',16);
771
772                 uni_curroffset += unilen;
773                 offset += VERSION3_REFERRAL_SIZE;
774         }
775         return reply_size;
776 }
777
778 /******************************************************************
779  Set up the Dfs referral for the dfs pathname
780 ******************************************************************/
781
782 int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char **ppdata)
783 {
784         struct junction_map junction;
785         int consumedcnt;
786         BOOL self_referral = False;
787         pstring buf;
788         int reply_size = 0;
789         char *pathnamep = pathname;
790         TALLOC_CTX *ctx;
791
792         if (!(ctx=talloc_init("setup_dfs_referral"))) {
793                 return -1;
794         }
795
796         ZERO_STRUCT(junction);
797
798         /* get the junction entry */
799         if (!pathnamep) {
800                 talloc_destroy(ctx);
801                 return -1;
802         }
803
804         /* Trim pathname sent by client so it begins with only one backslash.
805            Two backslashes confuse some dfs clients
806          */
807         while (pathnamep[0] == '\\' && pathnamep[1] == '\\') {
808                 pathnamep++;
809         }
810
811         pstrcpy(buf, pathnamep);
812         /* The following call can change cwd. */
813         if (!get_referred_path(ctx, buf, &junction, &consumedcnt, &self_referral)) {
814                 vfs_ChDir(orig_conn,orig_conn->connectpath);
815                 talloc_destroy(ctx);
816                 return -1;
817         }
818         vfs_ChDir(orig_conn,orig_conn->connectpath);
819         
820         if (!self_referral) {
821                 pathnamep[consumedcnt] = '\0';
822
823                 if( DEBUGLVL( 3 ) ) {
824                         int i=0;
825                         dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
826                         for(i=0;i<junction.referral_count;i++)
827                                 dbgtext(" %s",junction.referral_list[i].alternate_path);
828                         dbgtext(".\n");
829                 }
830         }
831
832         /* create the referral depeding on version */
833         DEBUG(10,("max_referral_level :%d\n",max_referral_level));
834         if(max_referral_level<2 || max_referral_level>3) {
835                 max_referral_level = 2;
836         }
837
838         switch(max_referral_level) {
839         case 2:
840                 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction, 
841                                                      consumedcnt, self_referral);
842                 break;
843         case 3:
844                 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction, 
845                                                      consumedcnt, self_referral);
846                 break;
847         default:
848                 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
849                 talloc_destroy(ctx);
850                 return -1;
851         }
852       
853         if (DEBUGLVL(10)) {
854                 DEBUGADD(0,("DFS Referral pdata:\n"));
855                 dump_data(0,*ppdata,reply_size);
856         }
857
858         talloc_destroy(ctx);
859         return reply_size;
860 }
861
862 /**********************************************************************
863  The following functions are called by the NETDFS RPC pipe functions
864  **********************************************************************/
865
866 /*********************************************************************
867  Creates a junction structure from a Dfs pathname
868 **********************************************************************/
869
870 BOOL create_junction(char *pathname, struct junction_map *jucn)
871 {
872         struct dfs_path dp;
873  
874         parse_dfs_path(pathname,&dp);
875
876         /* check if path is dfs : validate first token */
877         if ( !strequal(get_local_machine_name(),dp.hostname) ) {
878                 /* Hostname mismatch, check if one of our IP addresses */
879                 if (!ismyip(*interpret_addr2(dp.hostname))) {
880                         DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
881                                 dp.hostname, pathname));
882                         return False;
883                 }
884         }
885
886         /* Check for a non-DFS share */
887         if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
888                 DEBUG(4,("create_junction: %s is not an msdfs root.\n", dp.servicename));
889                 return False;
890         }
891
892         pstrcpy(jucn->service_name,dp.servicename);
893         pstrcpy(jucn->volume_name,dp.reqpath);
894         return True;
895 }
896
897 /**********************************************************************
898  Forms a valid Unix pathname from the junction 
899  **********************************************************************/
900
901 static BOOL junction_to_local_path(struct junction_map *jucn, char *path,
902                                    int max_pathlen, connection_struct *conn)
903 {
904         int snum;
905         pstring conn_path;
906
907         if(!path || !jucn) {
908                 return False;
909         }
910
911         snum = lp_servicenumber(jucn->service_name);
912         if(snum < 0) {
913                 return False;
914         }
915
916         safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
917         safe_strcat(path, "/", max_pathlen-1);
918         safe_strcat(path, jucn->volume_name, max_pathlen-1);
919
920         pstrcpy(conn_path, lp_pathname(snum));
921         if (!create_conn_struct(conn, snum, conn_path)) {
922                 return False;
923         }
924
925         return True;
926 }
927
928 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
929 {
930         pstring path;
931         pstring msdfs_link;
932         connection_struct conns;
933         connection_struct *conn = &conns;
934         int i=0;
935         BOOL insert_comma = False;
936         BOOL ret = False;
937
938         ZERO_STRUCT(conns);
939
940         if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
941                 return False;
942         }
943   
944         /* form the msdfs_link contents */
945         pstrcpy(msdfs_link, "msdfs:");
946         for(i=0; i<jucn->referral_count; i++) {
947                 char* refpath = jucn->referral_list[i].alternate_path;
948       
949                 trim_char(refpath, '\\', '\\');
950                 if(*refpath == '\0') {
951                         if (i == 0) {
952                                 insert_comma = False;
953                         }
954                         continue;
955                 }
956                 if (i > 0 && insert_comma) {
957                         pstrcat(msdfs_link, ",");
958                 }
959
960                 pstrcat(msdfs_link, refpath);
961                 if (!insert_comma) {
962                         insert_comma = True;
963                 }
964         }
965
966         DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
967
968         if(exists) {
969                 if(SMB_VFS_UNLINK(conn,path)!=0) {
970                         goto out;
971                 }
972         }
973
974         if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
975                 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", 
976                                 path, msdfs_link, strerror(errno)));
977                 goto out;
978         }
979         
980         
981         ret = True;
982         
983 out:
984
985         conn_free_internal(conn);
986         return ret;
987 }
988
989 BOOL remove_msdfs_link(struct junction_map *jucn)
990 {
991         pstring path;
992         connection_struct conns;
993         connection_struct *conn = &conns;
994         BOOL ret = False;
995
996         ZERO_STRUCT(conns);
997
998         if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
999                 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1000                         ret = True;
1001                 }
1002                 talloc_destroy( conn->mem_ctx );
1003         }
1004
1005         conn_free_internal(conn);
1006         return ret;
1007 }
1008
1009 static int form_junctions(TALLOC_CTX *ctx, int snum, struct junction_map *jucn, int jn_remain)
1010 {
1011         int cnt = 0;
1012         SMB_STRUCT_DIR *dirp;
1013         char* dname;
1014         pstring connect_path;
1015         char* service_name = lp_servicename(snum);
1016         connection_struct conn;
1017         struct referral *ref = NULL;
1018  
1019         ZERO_STRUCT(conn);
1020
1021         if (jn_remain <= 0) {
1022                 return 0;
1023         }
1024
1025         pstrcpy(connect_path,lp_pathname(snum));
1026
1027         if(*connect_path == '\0') {
1028                 return 0;
1029         }
1030
1031         /*
1032          * Fake up a connection struct for the VFS layer.
1033          */
1034
1035         if (!create_conn_struct(&conn, snum, connect_path)) {
1036                 return 0;
1037         }
1038
1039         /* form a junction for the msdfs root - convention 
1040            DO NOT REMOVE THIS: NT clients will not work with us
1041            if this is not present
1042         */ 
1043         pstrcpy(jucn[cnt].service_name, service_name);
1044         jucn[cnt].volume_name[0] = '\0';
1045         jucn[cnt].referral_count = 1;
1046
1047         ref = jucn[cnt].referral_list = TALLOC_P(ctx, struct referral);
1048         if (jucn[cnt].referral_list == NULL) {
1049                 DEBUG(0, ("Malloc failed!\n"));
1050                 goto out;
1051         }
1052
1053         ref->proximity = 0;
1054         ref->ttl = REFERRAL_TTL;
1055         if (*lp_msdfs_proxy(snum) != '\0') {
1056                 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
1057                 goto out;
1058         }
1059                 
1060         slprintf(ref->alternate_path, sizeof(pstring)-1,
1061                  "\\\\%s\\%s", get_local_machine_name(), service_name);
1062         cnt++;
1063
1064         /* Now enumerate all dfs links */
1065         dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1066         if(!dirp) {
1067                 goto out;
1068         }
1069
1070         while ((dname = vfs_readdirname(&conn, dirp)) != NULL) {
1071                 if (cnt >= jn_remain) {
1072                         SMB_VFS_CLOSEDIR(&conn,dirp);
1073                         DEBUG(2, ("ran out of MSDFS junction slots"));
1074                         goto out;
1075                 }
1076                 if (is_msdfs_link(ctx, &conn, dname, &jucn[cnt].referral_list,
1077                                   &jucn[cnt].referral_count, NULL)) {
1078                         pstrcpy(jucn[cnt].service_name, service_name);
1079                         pstrcpy(jucn[cnt].volume_name, dname);
1080                         cnt++;
1081                 }
1082         }
1083         
1084         SMB_VFS_CLOSEDIR(&conn,dirp);
1085
1086 out:
1087
1088         conn_free_internal(&conn);
1089         return cnt;
1090 }
1091
1092 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1093 {
1094         int i=0;
1095         int sharecount = 0;
1096         int jn_count = 0;
1097
1098         if(!lp_host_msdfs()) {
1099                 return 0;
1100         }
1101
1102         /* Ensure all the usershares are loaded. */
1103         become_root();
1104         sharecount = load_usershare_shares();
1105         unbecome_root();
1106
1107         for(i=0;i < sharecount && (jn_max - jn_count) > 0;i++) {
1108                 if(lp_msdfs_root(i)) {
1109                         jn_count += form_junctions(ctx, i,jucn,jn_max - jn_count);
1110                 }
1111         }
1112         return jn_count;
1113 }