missed one on BUG 1195; make sure to set the private * to NULL
[Samba/gebeck_regimport.git] / source3 / msdfs / msdfs.c
blob2df5fcf4f5acd5da1ace2afdae84342c02125543
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 MSDfs services for Samba
5 Copyright (C) Shirish Kalele 2000
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.
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.
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.
23 #include "includes.h"
25 extern fstring local_machine;
26 extern uint32 global_client_caps;
28 /**********************************************************************
29 Parse the pathname of the form \hostname\service\reqpath
30 into the dfs_path structure
31 **********************************************************************/
33 static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp)
35 pstring pathname_local;
36 char* p,*temp;
38 pstrcpy(pathname_local,pathname);
39 p = temp = pathname_local;
41 ZERO_STRUCTP(pdp);
43 trim_char(temp,'\\','\\');
44 DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp));
46 /* now tokenize */
47 /* parse out hostname */
48 p = strchr_m(temp,'\\');
49 if(p == NULL)
50 return False;
51 *p = '\0';
52 pstrcpy(pdp->hostname,temp);
53 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
55 /* parse out servicename */
56 temp = p+1;
57 p = strchr_m(temp,'\\');
58 if(p == NULL) {
59 pstrcpy(pdp->servicename,temp);
60 pdp->reqpath[0] = '\0';
61 return True;
63 *p = '\0';
64 pstrcpy(pdp->servicename,temp);
65 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
67 /* rest is reqpath */
68 check_path_syntax(pdp->reqpath, p+1);
70 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
71 return True;
74 /**********************************************************************
75 Parse the pathname of the form /hostname/service/reqpath
76 into the dfs_path structure
77 **********************************************************************/
79 static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path* pdp)
81 pstring pathname_local;
82 char* p,*temp;
84 pstrcpy(pathname_local,pathname);
85 p = temp = pathname_local;
87 ZERO_STRUCTP(pdp);
89 trim_char(temp,'/','/');
90 DEBUG(10,("temp in parse_processed_dfs_path: .%s. after trimming \\'s\n",temp));
92 /* now tokenize */
93 /* parse out hostname */
94 p = strchr_m(temp,'/');
95 if(p == NULL)
96 return False;
97 *p = '\0';
98 pstrcpy(pdp->hostname,temp);
99 DEBUG(10,("parse_processed_dfs_path: hostname: %s\n",pdp->hostname));
101 /* parse out servicename */
102 temp = p+1;
103 p = strchr_m(temp,'/');
104 if(p == NULL) {
105 pstrcpy(pdp->servicename,temp);
106 pdp->reqpath[0] = '\0';
107 return True;
109 *p = '\0';
110 pstrcpy(pdp->servicename,temp);
111 DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename));
113 /* rest is reqpath */
114 check_path_syntax(pdp->reqpath, p+1);
116 DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath));
117 return True;
120 /********************************************************
121 Fake up a connection struct for the VFS layer.
122 Note this CHANGES CWD !!!! JRA.
123 *********************************************************/
125 static BOOL create_conn_struct( connection_struct *conn, int snum, char *path)
127 ZERO_STRUCTP(conn);
128 conn->service = snum;
129 conn->connectpath = path;
130 pstring_sub(conn->connectpath , "%S", lp_servicename(snum));
132 /* needed for smbd_vfs_init() */
134 if ( (conn->mem_ctx=talloc_init("connection_struct")) == NULL ) {
135 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
136 return False;
139 if (!smbd_vfs_init(conn)) {
140 DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n"));
141 talloc_destroy( conn->mem_ctx );
142 return False;
146 * Windows seems to insist on doing trans2getdfsreferral() calls on the IPC$
147 * share as the anonymous user. If we try to chdir as that user we will
148 * fail.... WTF ? JRA.
151 if (vfs_ChDir(conn,conn->connectpath) != 0) {
152 DEBUG(3,("create_conn_struct: Can't ChDir to new conn path %s. Error was %s\n",
153 conn->connectpath, strerror(errno) ));
154 talloc_destroy( conn->mem_ctx );
155 return False;
157 return True;
161 /**********************************************************************
162 Parse the contents of a symlink to verify if it is an msdfs referral
163 A valid referral is of the form: msdfs:server1\share1,server2\share2
164 **********************************************************************/
166 static BOOL parse_symlink(char* buf,struct referral** preflist,
167 int* refcount)
169 pstring temp;
170 char* prot;
171 char* alt_path[MAX_REFERRAL_COUNT];
172 int count=0, i;
173 struct referral* reflist;
175 pstrcpy(temp,buf);
177 prot = strtok(temp,":");
179 if (!strequal(prot, "msdfs"))
180 return False;
182 /* No referral list requested. Just yes/no. */
183 if (!preflist)
184 return True;
186 /* parse out the alternate paths */
187 while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT)
188 count++;
190 DEBUG(10,("parse_symlink: count=%d\n", count));
192 reflist = *preflist = (struct referral*) malloc(count * sizeof(struct referral));
193 if(reflist == NULL) {
194 DEBUG(0,("parse_symlink: Malloc failed!\n"));
195 return False;
198 for(i=0;i<count;i++) {
199 char *p;
201 /* replace all /'s in the alternate path by a \ */
202 for(p = alt_path[i]; *p && ((p = strchr_m(p,'/'))!=NULL); p++) {
203 *p = '\\';
206 /* Remove leading '\\'s */
207 p = alt_path[i];
208 while (*p && (*p == '\\')) {
209 p++;
212 pstrcpy(reflist[i].alternate_path, "\\");
213 pstrcat(reflist[i].alternate_path, p);
214 reflist[i].proximity = 0;
215 reflist[i].ttl = REFERRAL_TTL;
216 DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path));
219 if(refcount)
220 *refcount = count;
222 return True;
225 /**********************************************************************
226 Returns true if the unix path is a valid msdfs symlink
227 **********************************************************************/
229 BOOL is_msdfs_link(connection_struct* conn, char * path,
230 struct referral** reflistp, int* refcnt,
231 SMB_STRUCT_STAT *sbufp)
233 SMB_STRUCT_STAT st;
234 pstring referral;
235 int referral_len = 0;
237 if (!path || !conn)
238 return False;
240 if (sbufp == NULL)
241 sbufp = &st;
243 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
244 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
245 return False;
248 if (S_ISLNK(sbufp->st_mode)) {
249 /* open the link and read it */
250 referral_len = SMB_VFS_READLINK(conn, path, referral,
251 sizeof(pstring));
252 if (referral_len == -1) {
253 DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno)));
254 return False;
257 referral[referral_len] = '\0';
258 DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral));
259 if (parse_symlink(referral, reflistp, refcnt))
260 return True;
262 return False;
265 /*****************************************************************
266 Used by other functions to decide if a dfs path is remote,
267 and to get the list of referred locations for that remote path.
269 findfirst_flag: For findfirsts, dfs links themselves are not
270 redirected, but paths beyond the links are. For normal smb calls,
271 even dfs links need to be redirected.
273 self_referralp: clients expect a dfs referral for the same share when
274 they request referrals for dfs roots on a server.
276 consumedcntp: how much of the dfs path is being redirected. the client
277 should try the remaining path on the redirected server.
279 *****************************************************************/
281 static BOOL resolve_dfs_path(pstring dfspath, struct dfs_path* dp,
282 connection_struct* conn,
283 BOOL findfirst_flag,
284 struct referral** reflistpp, int* refcntp,
285 BOOL* self_referralp, int* consumedcntp)
287 pstring localpath;
288 int consumed_level = 1;
289 char *p;
290 BOOL bad_path = False;
291 SMB_STRUCT_STAT sbuf;
292 pstring reqpath;
294 if (!dp || !conn) {
295 DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
296 return False;
299 if (dp->reqpath[0] == '\0') {
300 if (self_referralp) {
301 DEBUG(6,("resolve_dfs_path: self-referral. returning False\n"));
302 *self_referralp = True;
304 return False;
307 DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath));
309 unix_convert(dp->reqpath,conn,0,&bad_path,&sbuf);
310 /* JRA... should we strlower the last component here.... ? */
311 pstrcpy(localpath, dp->reqpath);
313 /* check if need to redirect */
314 if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
315 if (findfirst_flag) {
316 DEBUG(6,("resolve_dfs_path (FindFirst) No redirection "
317 "for dfs link %s.\n", dfspath));
318 return False;
319 } else {
320 DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n",
321 dfspath));
322 if (consumedcntp)
323 *consumedcntp = strlen(dfspath);
324 return True;
328 /* redirect if any component in the path is a link */
329 pstrcpy(reqpath, dp->reqpath);
330 p = strrchr_m(reqpath, '/');
331 while (p) {
332 *p = '\0';
333 pstrcpy(localpath, reqpath);
334 if (is_msdfs_link(conn, localpath, reflistpp, refcntp, NULL)) {
335 DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath));
337 /* To find the path consumed, we truncate the original
338 DFS pathname passed to use to remove the last
339 component. The length of the resulting string is
340 the path consumed
342 if (consumedcntp) {
343 char *q;
344 pstring buf;
345 pstrcpy(buf, dfspath);
346 trim_char(buf, '\0', '\\');
347 for (; consumed_level; consumed_level--) {
348 q = strrchr_m(buf, '\\');
349 if (q)
350 *q = 0;
352 *consumedcntp = strlen(buf);
353 DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
356 return True;
358 p = strrchr_m(reqpath, '/');
359 consumed_level++;
362 return False;
365 /*****************************************************************
366 Decides if a dfs pathname should be redirected or not.
367 If not, the pathname is converted to a tcon-relative local unix path
368 *****************************************************************/
370 BOOL dfs_redirect(pstring pathname, connection_struct* conn,
371 BOOL findfirst_flag)
373 struct dfs_path dp;
375 if (!conn || !pathname)
376 return False;
378 parse_processed_dfs_path(pathname, &dp);
380 /* if dfs pathname for a non-dfs share, convert to tcon-relative
381 path and return false */
382 if (!lp_msdfs_root(SNUM(conn))) {
383 pstrcpy(pathname, dp.reqpath);
384 return False;
387 if (!strequal(dp.servicename, lp_servicename(SNUM(conn)) ))
388 return False;
390 if (resolve_dfs_path(pathname, &dp, conn, findfirst_flag,
391 NULL, NULL, NULL, NULL)) {
392 DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname));
393 return True;
394 } else {
395 DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname));
397 /* Form non-dfs tcon-relative path */
398 pstrcpy(pathname, dp.reqpath);
399 DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n",
400 pathname));
401 return False;
404 /* never reached */
407 /**********************************************************************
408 Return a self referral.
409 **********************************************************************/
411 static BOOL self_ref(char *pathname, struct junction_map *jucn,
412 int *consumedcntp, BOOL *self_referralp)
414 struct referral *ref;
416 if (self_referralp != NULL)
417 *self_referralp = True;
419 jucn->referral_count = 1;
420 if((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
421 DEBUG(0,("self_ref: malloc failed for referral\n"));
422 return False;
425 pstrcpy(ref->alternate_path,pathname);
426 ref->proximity = 0;
427 ref->ttl = REFERRAL_TTL;
428 jucn->referral_list = ref;
429 if (consumedcntp)
430 *consumedcntp = strlen(pathname);
432 return True;
435 /**********************************************************************
436 Gets valid referrals for a dfs path and fills up the
437 junction_map structure
438 **********************************************************************/
440 BOOL get_referred_path(char *pathname, struct junction_map *jucn,
441 int *consumedcntp, BOOL *self_referralp)
443 struct dfs_path dp;
445 struct connection_struct conns;
446 struct connection_struct* conn = &conns;
447 pstring conn_path;
448 int snum;
449 BOOL ret = False;
450 BOOL self_referral = False;
452 if (!pathname || !jucn)
453 return False;
455 ZERO_STRUCT(conns);
457 if (self_referralp)
458 *self_referralp = False;
459 else
460 self_referralp = &self_referral;
462 parse_dfs_path(pathname, &dp);
464 /* Verify hostname in path */
465 if (local_machine && (!strequal(local_machine, dp.hostname))) {
466 /* Hostname mismatch, check if one of our IP addresses */
467 if (!ismyip(*interpret_addr2(dp.hostname))) {
468 DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n",
469 dp.hostname, pathname));
470 return False;
474 pstrcpy(jucn->service_name, dp.servicename);
475 pstrcpy(jucn->volume_name, dp.reqpath);
477 /* Verify the share is a dfs root */
478 snum = lp_servicenumber(jucn->service_name);
479 if(snum < 0) {
480 if ((snum = find_service(jucn->service_name)) < 0)
481 return False;
485 * Self referrals are tested with a anonymous IPC connection and
486 * a GET_DFS_REFERRAL call to \\server\share. (which means dp.reqpath[0] points
487 * to an empty string). create_conn_struct cd's into the directory and will
488 * fail if it cannot (as the anonymous user). Cope with this.
491 if (dp.reqpath[0] == '\0') {
492 return self_ref(pathname, jucn, consumedcntp, self_referralp);
495 pstrcpy(conn_path, lp_pathname(snum));
496 if (!create_conn_struct(conn, snum, conn_path))
497 return False;
499 if (!lp_msdfs_root(SNUM(conn))) {
500 DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n",
501 dp.servicename, pathname));
502 goto out;
505 if (*lp_msdfs_proxy(snum) != '\0') {
506 struct referral* ref;
507 jucn->referral_count = 1;
508 if ((ref = (struct referral*) malloc(sizeof(struct referral))) == NULL) {
509 DEBUG(0, ("malloc failed for referral\n"));
510 goto out;
513 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
514 if (dp.reqpath[0] != '\0')
515 pstrcat(ref->alternate_path, dp.reqpath);
516 ref->proximity = 0;
517 ref->ttl = REFERRAL_TTL;
518 jucn->referral_list = ref;
519 if (consumedcntp)
520 *consumedcntp = strlen(pathname);
521 ret = True;
522 goto out;
525 /* If not remote & not a self referral, return False */
526 if (!resolve_dfs_path(pathname, &dp, conn, False,
527 &jucn->referral_list, &jucn->referral_count,
528 self_referralp, consumedcntp)) {
529 if (!*self_referralp) {
530 DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname));
531 goto out;
535 /* if self_referral, fill up the junction map */
536 if (*self_referralp) {
537 if (self_ref(pathname, jucn, consumedcntp, self_referralp) == False) {
538 goto out;
542 ret = True;
544 out:
545 if (conn->mem_ctx)
546 talloc_destroy( conn->mem_ctx );
548 return ret;
551 static int setup_ver2_dfs_referral(char* pathname, char** ppdata,
552 struct junction_map* junction,
553 int consumedcnt,
554 BOOL self_referral)
556 char* pdata = *ppdata;
558 unsigned char uni_requestedpath[1024];
559 int uni_reqpathoffset1,uni_reqpathoffset2;
560 int uni_curroffset;
561 int requestedpathlen=0;
562 int offset;
563 int reply_size = 0;
564 int i=0;
566 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
568 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
569 STR_TERMINATE);
571 dump_data(10, (const char *) uni_requestedpath,requestedpathlen);
573 DEBUG(10,("ref count = %u\n",junction->referral_count));
575 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
576 VERSION2_REFERRAL_SIZE * junction->referral_count;
578 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
580 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
582 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
583 2 * requestedpathlen;
584 DEBUG(10,("reply_size: %u\n",reply_size));
586 /* add up the unicode lengths of all the referral paths */
587 for(i=0;i<junction->referral_count;i++) {
588 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
589 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
592 DEBUG(10,("reply_size = %u\n",reply_size));
593 /* add the unexplained 0x16 bytes */
594 reply_size += 0x16;
596 pdata = Realloc(pdata,reply_size);
597 if(pdata == NULL) {
598 DEBUG(0,("malloc failed for Realloc!\n"));
599 return -1;
600 } else
601 *ppdata = pdata;
603 /* copy in the dfs requested paths.. required for offset calculations */
604 memcpy(pdata+uni_reqpathoffset1,uni_requestedpath,requestedpathlen);
605 memcpy(pdata+uni_reqpathoffset2,uni_requestedpath,requestedpathlen);
607 /* create the header */
608 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
609 SSVAL(pdata,2,junction->referral_count); /* number of referral in this pkt */
610 if(self_referral)
611 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
612 else
613 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
615 offset = 8;
616 /* add the referral elements */
617 for(i=0;i<junction->referral_count;i++) {
618 struct referral* ref = &junction->referral_list[i];
619 int unilen;
621 SSVAL(pdata,offset,2); /* version 2 */
622 SSVAL(pdata,offset+2,VERSION2_REFERRAL_SIZE);
623 if(self_referral)
624 SSVAL(pdata,offset+4,1);
625 else
626 SSVAL(pdata,offset+4,0);
627 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
628 SIVAL(pdata,offset+8,ref->proximity);
629 SIVAL(pdata,offset+12,ref->ttl);
631 SSVAL(pdata,offset+16,uni_reqpathoffset1-offset);
632 SSVAL(pdata,offset+18,uni_reqpathoffset2-offset);
633 /* copy referred path into current offset */
634 unilen = rpcstr_push(pdata+uni_curroffset, ref->alternate_path,
635 -1, STR_UNICODE);
637 SSVAL(pdata,offset+20,uni_curroffset-offset);
639 uni_curroffset += unilen;
640 offset += VERSION2_REFERRAL_SIZE;
642 /* add in the unexplained 22 (0x16) bytes at the end */
643 memset(pdata+uni_curroffset,'\0',0x16);
644 return reply_size;
647 static int setup_ver3_dfs_referral(char* pathname, char** ppdata,
648 struct junction_map* junction,
649 int consumedcnt,
650 BOOL self_referral)
652 char* pdata = *ppdata;
654 unsigned char uni_reqpath[1024];
655 int uni_reqpathoffset1, uni_reqpathoffset2;
656 int uni_curroffset;
657 int reply_size = 0;
659 int reqpathlen = 0;
660 int offset,i=0;
662 DEBUG(10,("setting up version3 referral\n"));
664 reqpathlen = rpcstr_push(uni_reqpath, pathname, -1, STR_TERMINATE);
666 dump_data(10, (char *) uni_reqpath,reqpathlen);
668 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE + VERSION3_REFERRAL_SIZE * junction->referral_count;
669 uni_reqpathoffset2 = uni_reqpathoffset1 + reqpathlen;
670 reply_size = uni_curroffset = uni_reqpathoffset2 + reqpathlen;
672 for(i=0;i<junction->referral_count;i++) {
673 DEBUG(10,("referral %u : %s\n",i,junction->referral_list[i].alternate_path));
674 reply_size += (strlen(junction->referral_list[i].alternate_path)+1)*2;
677 pdata = Realloc(pdata,reply_size);
678 if(pdata == NULL) {
679 DEBUG(0,("version3 referral setup: malloc failed for Realloc!\n"));
680 return -1;
681 } else
682 *ppdata = pdata;
684 /* create the header */
685 SSVAL(pdata,0,consumedcnt * 2); /* path consumed */
686 SSVAL(pdata,2,junction->referral_count); /* number of referral */
687 if(self_referral)
688 SIVAL(pdata,4,DFSREF_REFERRAL_SERVER | DFSREF_STORAGE_SERVER);
689 else
690 SIVAL(pdata,4,DFSREF_STORAGE_SERVER);
692 /* copy in the reqpaths */
693 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
694 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
696 offset = 8;
697 for(i=0;i<junction->referral_count;i++) {
698 struct referral* ref = &(junction->referral_list[i]);
699 int unilen;
701 SSVAL(pdata,offset,3); /* version 3 */
702 SSVAL(pdata,offset+2,VERSION3_REFERRAL_SIZE);
703 if(self_referral)
704 SSVAL(pdata,offset+4,1);
705 else
706 SSVAL(pdata,offset+4,0);
708 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
709 SIVAL(pdata,offset+8,ref->ttl);
711 SSVAL(pdata,offset+12,uni_reqpathoffset1-offset);
712 SSVAL(pdata,offset+14,uni_reqpathoffset2-offset);
713 /* copy referred path into current offset */
714 unilen = rpcstr_push(pdata+uni_curroffset,ref->alternate_path,
715 -1, STR_UNICODE | STR_TERMINATE);
716 SSVAL(pdata,offset+16,uni_curroffset-offset);
717 /* copy 0x10 bytes of 00's in the ServiceSite GUID */
718 memset(pdata+offset+18,'\0',16);
720 uni_curroffset += unilen;
721 offset += VERSION3_REFERRAL_SIZE;
723 return reply_size;
726 /******************************************************************
727 * Set up the Dfs referral for the dfs pathname
728 ******************************************************************/
730 int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char** ppdata)
732 struct junction_map junction;
733 int consumedcnt;
734 BOOL self_referral = False;
735 pstring buf;
736 int reply_size = 0;
737 char *pathnamep = pathname;
739 ZERO_STRUCT(junction);
741 /* get the junction entry */
742 if (!pathnamep)
743 return -1;
745 /* Trim pathname sent by client so it begins with only one backslash.
746 Two backslashes confuse some dfs clients
748 while (pathnamep[0] == '\\' && pathnamep[1] == '\\')
749 pathnamep++;
751 pstrcpy(buf, pathnamep);
752 /* The following call can change cwd. */
753 if (!get_referred_path(buf, &junction, &consumedcnt, &self_referral)) {
754 vfs_ChDir(orig_conn,orig_conn->connectpath);
755 return -1;
757 vfs_ChDir(orig_conn,orig_conn->connectpath);
759 if (!self_referral) {
760 pathnamep[consumedcnt] = '\0';
762 if( DEBUGLVL( 3 ) ) {
763 int i=0;
764 dbgtext("setup_dfs_referral: Path %s to alternate path(s):",pathnamep);
765 for(i=0;i<junction.referral_count;i++)
766 dbgtext(" %s",junction.referral_list[i].alternate_path);
767 dbgtext(".\n");
771 /* create the referral depeding on version */
772 DEBUG(10,("max_referral_level :%d\n",max_referral_level));
773 if(max_referral_level<2 || max_referral_level>3)
774 max_referral_level = 2;
776 switch(max_referral_level) {
777 case 2:
778 reply_size = setup_ver2_dfs_referral(pathnamep, ppdata, &junction,
779 consumedcnt, self_referral);
780 SAFE_FREE(junction.referral_list);
781 break;
782 case 3:
783 reply_size = setup_ver3_dfs_referral(pathnamep, ppdata, &junction,
784 consumedcnt, self_referral);
785 SAFE_FREE(junction.referral_list);
786 break;
787 default:
788 DEBUG(0,("setup_dfs_referral: Invalid dfs referral version: %d\n", max_referral_level));
789 return -1;
792 DEBUG(10,("DFS Referral pdata:\n"));
793 dump_data(10,*ppdata,reply_size);
794 return reply_size;
797 /**********************************************************************
798 The following functions are called by the NETDFS RPC pipe functions
799 **********************************************************************/
801 /**********************************************************************
802 Creates a junction structure from a Dfs pathname
803 **********************************************************************/
804 BOOL create_junction(char* pathname, struct junction_map* jucn)
806 struct dfs_path dp;
808 parse_dfs_path(pathname,&dp);
810 /* check if path is dfs : validate first token */
811 if (local_machine && (!strequal(local_machine,dp.hostname))) {
813 /* Hostname mismatch, check if one of our IP addresses */
814 if (!ismyip(*interpret_addr2(dp.hostname))) {
815 DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n",
816 dp.hostname, pathname));
817 return False;
821 /* Check for a non-DFS share */
822 if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) {
823 DEBUG(4,("create_junction: %s is not an msdfs root.\n",
824 dp.servicename));
825 return False;
828 pstrcpy(jucn->service_name,dp.servicename);
829 pstrcpy(jucn->volume_name,dp.reqpath);
830 return True;
833 /**********************************************************************
834 Forms a valid Unix pathname from the junction
835 **********************************************************************/
837 static BOOL junction_to_local_path(struct junction_map* jucn, char* path,
838 int max_pathlen, connection_struct *conn)
840 int snum;
841 pstring conn_path;
843 if(!path || !jucn)
844 return False;
846 snum = lp_servicenumber(jucn->service_name);
847 if(snum < 0)
848 return False;
850 safe_strcpy(path, lp_pathname(snum), max_pathlen-1);
851 safe_strcat(path, "/", max_pathlen-1);
852 safe_strcat(path, jucn->volume_name, max_pathlen-1);
854 pstrcpy(conn_path, lp_pathname(snum));
855 if (!create_conn_struct(conn, snum, conn_path))
856 return False;
858 return True;
861 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
863 pstring path;
864 pstring msdfs_link;
865 connection_struct conns;
866 connection_struct *conn = &conns;
867 int i=0;
868 BOOL insert_comma = False;
869 BOOL ret = False;
871 if(!junction_to_local_path(jucn, path, sizeof(path), conn))
872 return False;
874 /* form the msdfs_link contents */
875 pstrcpy(msdfs_link, "msdfs:");
876 for(i=0; i<jucn->referral_count; i++) {
877 char* refpath = jucn->referral_list[i].alternate_path;
879 trim_char(refpath, '\\', '\\');
880 if(*refpath == '\0') {
881 if (i == 0)
882 insert_comma = False;
883 continue;
885 if (i > 0 && insert_comma)
886 pstrcat(msdfs_link, ",");
888 pstrcat(msdfs_link, refpath);
889 if (!insert_comma)
890 insert_comma = True;
894 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
896 if(exists)
897 if(SMB_VFS_UNLINK(conn,path)!=0)
898 goto out;
900 if(SMB_VFS_SYMLINK(conn, msdfs_link, path) < 0) {
901 DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n",
902 path, msdfs_link, strerror(errno)));
903 goto out;
907 ret = True;
909 out:
910 talloc_destroy( conn->mem_ctx );
911 return ret;
914 BOOL remove_msdfs_link(struct junction_map* jucn)
916 pstring path;
917 connection_struct conns;
918 connection_struct *conn = &conns;
919 BOOL ret = False;
921 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
922 if( SMB_VFS_UNLINK(conn, path) == 0 )
923 ret = True;
925 talloc_destroy( conn->mem_ctx );
928 return ret;
931 static BOOL form_junctions(int snum, struct junction_map* jucn, int* jn_count)
933 int cnt = *jn_count;
934 DIR *dirp;
935 char* dname;
936 pstring connect_path;
937 char* service_name = lp_servicename(snum);
938 connection_struct conns;
939 connection_struct *conn = &conns;
940 struct referral *ref = NULL;
941 BOOL ret = False;
943 pstrcpy(connect_path,lp_pathname(snum));
945 if(*connect_path == '\0')
946 return False;
949 * Fake up a connection struct for the VFS layer.
952 if (!create_conn_struct(conn, snum, connect_path))
953 return False;
955 /* form a junction for the msdfs root - convention
956 DO NOT REMOVE THIS: NT clients will not work with us
957 if this is not present
959 pstrcpy(jucn[cnt].service_name, service_name);
960 jucn[cnt].volume_name[0] = '\0';
961 jucn[cnt].referral_count = 1;
963 ref = jucn[cnt].referral_list
964 = (struct referral*) malloc(sizeof(struct referral));
965 if (jucn[cnt].referral_list == NULL) {
966 DEBUG(0, ("Malloc failed!\n"));
967 goto out;
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;
979 slprintf(ref->alternate_path, sizeof(pstring)-1,
980 "\\\\%s\\%s", local_machine, service_name);
981 cnt++;
983 /* Now enumerate all dfs links */
984 dirp = SMB_VFS_OPENDIR(conn, ".");
985 if(!dirp)
986 goto out;
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++;
997 SMB_VFS_CLOSEDIR(conn,dirp);
998 *jn_count = cnt;
999 out:
1000 talloc_destroy(conn->mem_ctx);
1001 return ret;
1004 int enum_msdfs_links(struct junction_map* jucn)
1006 int i=0;
1007 int jn_count = 0;
1009 if(!lp_host_msdfs())
1010 return 0;
1012 for(i=0;i < lp_numservices();i++) {
1013 if(lp_msdfs_root(i))
1014 form_junctions(i,jucn,&jn_count);
1016 return jn_count;