r14336: Try and quieten coverity #53 and #54. Make it obvious
[Samba/gebeck_regimport.git] / source3 / smbd / msdfs.c
blob257a6105c5df4fa82d616a9a8e8e070c1c369e16
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 #define DBGC_CLASS DBGC_MSDFS
24 #include "includes.h"
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;
52 *p = '\0';
53 pstrcpy(pdp->hostname,temp);
54 DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname));
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;
64 *p = '\0';
65 pstrcpy(pdp->servicename,temp);
66 DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename));
68 /* rest is reqpath */
69 check_path_syntax(pdp->reqpath, p+1);
71 DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath));
72 return True;
75 /**********************************************************************
76 Parse the pathname of the form /hostname/service/reqpath
77 into the dfs_path structure
78 **********************************************************************/
80 static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path *pdp, BOOL allow_wcards)
82 pstring pathname_local;
83 char *p,*temp;
85 pstrcpy(pathname_local,pathname);
86 p = temp = pathname_local;
88 ZERO_STRUCTP(pdp);
90 trim_char(temp,'/','/');
91 DEBUG(10,("temp in parse_processed_dfs_path: .%s. after trimming \\'s\n",temp));
93 /* now tokenize */
94 /* parse out hostname */
95 p = strchr_m(temp,'/');
96 if(p == NULL) {
97 return False;
99 *p = '\0';
100 pstrcpy(pdp->hostname,temp);
101 DEBUG(10,("parse_processed_dfs_path: hostname: %s\n",pdp->hostname));
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;
111 *p = '\0';
112 pstrcpy(pdp->servicename,temp);
113 DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename));
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);
123 DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath));
124 return True;
127 /********************************************************
128 Fake up a connection struct for the VFS layer.
129 Note this CHANGES CWD !!!! JRA.
130 *********************************************************/
132 static BOOL create_conn_struct(connection_struct *conn, int snum, char *path)
134 pstring connpath;
136 ZERO_STRUCTP(conn);
138 conn->service = snum;
139 pstrcpy(connpath, path);
140 pstring_sub(connpath , "%S", lp_servicename(snum));
142 /* needed for smbd_vfs_init() */
144 if ( (conn->mem_ctx=talloc_init("connection_struct")) == NULL ) {
145 DEBUG(0,("talloc_init(connection_struct) failed!\n"));
146 return False;
149 set_conn_connectpath(conn, connpath);
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;
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.
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;
170 return True;
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 **********************************************************************/
179 static BOOL parse_symlink(TALLOC_CTX *ctx, char *buf, struct referral **preflist, int *refcount)
181 pstring temp;
182 char *prot;
183 char *alt_path[MAX_REFERRAL_COUNT];
184 int count = 0, i;
185 struct referral *reflist;
187 pstrcpy(temp,buf);
189 prot = strtok(temp,":");
191 if (!strequal(prot, "msdfs")) {
192 return False;
195 /* No referral list requested. Just yes/no. */
196 if (!preflist) {
197 return True;
200 if (!ctx) {
201 DEBUG(0,("parse_symlink: logic error. TALLOC_CTX should not be null.\n"));
202 return True;
205 /* parse out the alternate paths */
206 while(((alt_path[count] = strtok(NULL,",")) != NULL) && count<MAX_REFERRAL_COUNT) {
207 count++;
210 DEBUG(10,("parse_symlink: count=%d\n", count));
212 reflist = *preflist = TALLOC_ARRAY(ctx, struct referral, count);
213 if(reflist == NULL) {
214 DEBUG(0,("parse_symlink: talloc failed!\n"));
215 return False;
218 for(i=0;i<count;i++) {
219 char *p;
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 = '\\';
226 /* Remove leading '\\'s */
227 p = alt_path[i];
228 while (*p && (*p == '\\')) {
229 p++;
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));
239 if(refcount) {
240 *refcount = count;
243 return True;
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 **********************************************************************/
251 BOOL is_msdfs_link(TALLOC_CTX *ctx, connection_struct *conn, char *path,
252 struct referral **reflistp, int *refcnt,
253 SMB_STRUCT_STAT *sbufp)
255 SMB_STRUCT_STAT st;
256 pstring referral;
257 int referral_len = 0;
259 if (!path || !conn) {
260 return False;
263 if (sbufp == NULL) {
264 sbufp = &st;
267 if (SMB_VFS_LSTAT(conn, path, sbufp) != 0) {
268 DEBUG(5,("is_msdfs_link: %s does not exist.\n",path));
269 return False;
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;
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;
286 return False;
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.
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.
297 self_referralp: clients expect a dfs referral for the same share when
298 they request referrals for dfs roots on a server.
300 consumedcntp: how much of the dfs path is being redirected. the client
301 should try the remaining path on the redirected server.
303 TALLOC_CTX can be NULL here if struct referral **reflistpp, int *refcntp
304 are also NULL.
305 *****************************************************************/
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)
312 pstring localpath;
313 int consumed_level = 1;
314 char *p;
315 BOOL bad_path = False;
316 SMB_STRUCT_STAT sbuf;
317 pstring reqpath;
319 if (!dp || !conn) {
320 DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n"));
321 return False;
324 if (!ctx && (reflistpp || refcntp)) {
325 DEBUG(0,("resolve_dfs_path: logic error. TALLOC_CTX must not be NULL.\n"));
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;
333 return False;
336 DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath));
338 unix_convert(dp->reqpath,conn,0,&bad_path,&sbuf);
339 /* JRA... should we strlower the last component here.... ? */
340 pstrcpy(localpath, dp->reqpath);
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;
350 DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n", dfspath));
351 if (consumedcntp) {
352 *consumedcntp = strlen(dfspath);
354 return True;
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));
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
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;
383 *consumedcntp = strlen(buf);
384 DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp));
387 return True;
389 p = strrchr_m(reqpath, '/');
390 consumed_level++;
393 return False;
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
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 *****************************************************************/
405 BOOL dfs_redirect( pstring pathname, connection_struct *conn, BOOL search_wcard_flag )
407 struct dfs_path dp;
409 if (!conn || !pathname) {
410 return False;
413 parse_processed_dfs_path(pathname, &dp, search_wcard_flag);
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;
422 if (!strequal(dp.servicename, lp_servicename(SNUM(conn)) )) {
423 return False;
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));
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;
439 /* never reached */
442 /**********************************************************************
443 Return a self referral.
444 **********************************************************************/
446 static BOOL self_ref(TALLOC_CTX *ctx, char *pathname, struct junction_map *jucn,
447 int *consumedcntp, BOOL *self_referralp)
449 struct referral *ref;
451 if (self_referralp != NULL) {
452 *self_referralp = True;
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;
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);
469 return True;
472 /**********************************************************************
473 Gets valid referrals for a dfs path and fills up the
474 junction_map structure.
475 **********************************************************************/
477 BOOL get_referred_path(TALLOC_CTX *ctx, char *pathname, struct junction_map *jucn,
478 int *consumedcntp, BOOL *self_referralp)
480 struct dfs_path dp;
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;
489 if (!pathname || !jucn) {
490 return False;
493 ZERO_STRUCT(conns);
495 if (self_referralp) {
496 *self_referralp = False;
497 } else {
498 self_referralp = &self_referral;
501 parse_dfs_path(pathname, &dp);
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;
513 pstrcpy(jucn->service_name, dp.servicename);
514 pstrcpy(jucn->volume_name, dp.reqpath);
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;
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;
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.
537 if (dp.reqpath[0] == '\0') {
539 struct referral* ref;
541 if (*lp_msdfs_proxy(snum) == '\0') {
542 return self_ref(ctx, pathname, jucn, consumedcntp, self_referralp);
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;
551 pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum));
552 if (dp.reqpath[0] != '\0') {
553 pstrcat(ref->alternate_path, dp.reqpath);
555 ref->proximity = 0;
556 ref->ttl = REFERRAL_TTL;
557 jucn->referral_list = ref;
558 if (consumedcntp) {
559 *consumedcntp = strlen(pathname);
561 ret = True;
562 goto out;
565 pstrcpy(conn_path, lp_pathname(snum));
566 if (!create_conn_struct(conn, snum, conn_path)) {
567 return False;
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;
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;
587 ret = True;
589 out:
591 conn_free_internal(conn);
592 return ret;
595 static int setup_ver2_dfs_referral(char *pathname, char **ppdata,
596 struct junction_map *junction,
597 int consumedcnt,
598 BOOL self_referral)
600 char* pdata = *ppdata;
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;
610 DEBUG(10,("setting up version2 referral\nRequested path:\n"));
612 requestedpathlen = rpcstr_push(uni_requestedpath, pathname, -1,
613 STR_TERMINATE);
615 if (DEBUGLVL(10)) {
616 dump_data(0, (const char *) uni_requestedpath,requestedpathlen);
619 DEBUG(10,("ref count = %u\n",junction->referral_count));
621 uni_reqpathoffset1 = REFERRAL_HEADER_SIZE +
622 VERSION2_REFERRAL_SIZE * junction->referral_count;
624 uni_reqpathoffset2 = uni_reqpathoffset1 + requestedpathlen;
626 uni_curroffset = uni_reqpathoffset2 + requestedpathlen;
628 reply_size = REFERRAL_HEADER_SIZE + VERSION2_REFERRAL_SIZE*junction->referral_count +
629 2 * requestedpathlen;
630 DEBUG(10,("reply_size: %u\n",reply_size));
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;
638 DEBUG(10,("reply_size = %u\n",reply_size));
639 /* add the unexplained 0x16 bytes */
640 reply_size += 0x16;
642 pdata = SMB_REALLOC(pdata,reply_size);
643 if(pdata == NULL) {
644 DEBUG(0,("malloc failed for Realloc!\n"));
645 return -1;
647 *ppdata = pdata;
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);
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);
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;
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);
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);
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);
685 SSVAL(pdata,offset+20,uni_curroffset-offset);
687 uni_curroffset += unilen;
688 offset += VERSION2_REFERRAL_SIZE;
690 /* add in the unexplained 22 (0x16) bytes at the end */
691 memset(pdata+uni_curroffset,'\0',0x16);
692 return reply_size;
695 static int setup_ver3_dfs_referral(char *pathname, char **ppdata,
696 struct junction_map *junction,
697 int consumedcnt,
698 BOOL self_referral)
700 char* pdata = *ppdata;
702 unsigned char uni_reqpath[1024];
703 int uni_reqpathoffset1, uni_reqpathoffset2;
704 int uni_curroffset;
705 int reply_size = 0;
707 int reqpathlen = 0;
708 int offset,i=0;
710 DEBUG(10,("setting up version3 referral\n"));
712 reqpathlen = rpcstr_push(uni_reqpath, pathname, (size_t)-1, STR_TERMINATE);
714 if (DEBUGLVL(10)) {
715 dump_data(0, (char *) uni_reqpath,reqpathlen);
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;
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;
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;
732 *ppdata = pdata;
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);
743 /* copy in the reqpaths */
744 memcpy(pdata+uni_reqpathoffset1,uni_reqpath,reqpathlen);
745 memcpy(pdata+uni_reqpathoffset2,uni_reqpath,reqpathlen);
747 offset = 8;
748 for(i=0;i<junction->referral_count;i++) {
749 struct referral* ref = &(junction->referral_list[i]);
750 int unilen;
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);
760 SSVAL(pdata,offset+6,0); /* ref_flags :use path_consumed bytes? */
761 SIVAL(pdata,offset+8,ref->ttl);
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);
772 uni_curroffset += unilen;
773 offset += VERSION3_REFERRAL_SIZE;
775 return reply_size;
778 /******************************************************************
779 Set up the Dfs referral for the dfs pathname
780 ******************************************************************/
782 int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char **ppdata)
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;
792 if (!(ctx=talloc_init("setup_dfs_referral"))) {
793 return -1;
796 ZERO_STRUCT(junction);
798 /* get the junction entry */
799 if (!pathnamep) {
800 talloc_destroy(ctx);
801 return -1;
804 /* Trim pathname sent by client so it begins with only one backslash.
805 Two backslashes confuse some dfs clients
807 while (pathnamep[0] == '\\' && pathnamep[1] == '\\') {
808 pathnamep++;
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;
818 vfs_ChDir(orig_conn,orig_conn->connectpath);
820 if (!self_referral) {
821 pathnamep[consumedcnt] = '\0';
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");
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;
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;
853 if (DEBUGLVL(10)) {
854 DEBUGADD(0,("DFS Referral pdata:\n"));
855 dump_data(0,*ppdata,reply_size);
858 talloc_destroy(ctx);
859 return reply_size;
862 /**********************************************************************
863 The following functions are called by the NETDFS RPC pipe functions
864 **********************************************************************/
866 /*********************************************************************
867 Creates a junction structure from a Dfs pathname
868 **********************************************************************/
870 BOOL create_junction(char *pathname, struct junction_map *jucn)
872 struct dfs_path dp;
874 parse_dfs_path(pathname,&dp);
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;
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;
892 pstrcpy(jucn->service_name,dp.servicename);
893 pstrcpy(jucn->volume_name,dp.reqpath);
894 return True;
897 /**********************************************************************
898 Forms a valid Unix pathname from the junction
899 **********************************************************************/
901 static BOOL junction_to_local_path(struct junction_map *jucn, char *path,
902 int max_pathlen, connection_struct *conn)
904 int snum;
905 pstring conn_path;
907 if(!path || !jucn) {
908 return False;
911 snum = lp_servicenumber(jucn->service_name);
912 if(snum < 0) {
913 return False;
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);
920 pstrcpy(conn_path, lp_pathname(snum));
921 if (!create_conn_struct(conn, snum, conn_path)) {
922 return False;
925 return True;
928 BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists)
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;
938 ZERO_STRUCT(conns);
940 if(!junction_to_local_path(jucn, path, sizeof(path), conn)) {
941 return False;
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;
949 trim_char(refpath, '\\', '\\');
950 if(*refpath == '\0') {
951 if (i == 0) {
952 insert_comma = False;
954 continue;
956 if (i > 0 && insert_comma) {
957 pstrcat(msdfs_link, ",");
960 pstrcat(msdfs_link, refpath);
961 if (!insert_comma) {
962 insert_comma = True;
966 DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link));
968 if(exists) {
969 if(SMB_VFS_UNLINK(conn,path)!=0) {
970 goto out;
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;
981 ret = True;
983 out:
985 conn_free_internal(conn);
986 return ret;
989 BOOL remove_msdfs_link(struct junction_map *jucn)
991 pstring path;
992 connection_struct conns;
993 connection_struct *conn = &conns;
994 BOOL ret = False;
996 ZERO_STRUCT(conns);
998 if( junction_to_local_path(jucn, path, sizeof(path), conn) ) {
999 if( SMB_VFS_UNLINK(conn, path) == 0 ) {
1000 ret = True;
1002 talloc_destroy( conn->mem_ctx );
1005 conn_free_internal(conn);
1006 return ret;
1009 static int form_junctions(TALLOC_CTX *ctx, int snum, struct junction_map *jucn, int jn_remain)
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;
1019 ZERO_STRUCT(conn);
1021 if (jn_remain <= 0) {
1022 return 0;
1025 pstrcpy(connect_path,lp_pathname(snum));
1027 if(*connect_path == '\0') {
1028 return 0;
1032 * Fake up a connection struct for the VFS layer.
1035 if (!create_conn_struct(&conn, snum, connect_path)) {
1036 return 0;
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
1043 pstrcpy(jucn[cnt].service_name, service_name);
1044 jucn[cnt].volume_name[0] = '\0';
1045 jucn[cnt].referral_count = 1;
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;
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;
1060 slprintf(ref->alternate_path, sizeof(pstring)-1,
1061 "\\\\%s\\%s", get_local_machine_name(), service_name);
1062 cnt++;
1064 /* Now enumerate all dfs links */
1065 dirp = SMB_VFS_OPENDIR(&conn, ".", NULL, 0);
1066 if(!dirp) {
1067 goto out;
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;
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++;
1084 SMB_VFS_CLOSEDIR(&conn,dirp);
1086 out:
1088 conn_free_internal(&conn);
1089 return cnt;
1092 int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max)
1094 int i=0;
1095 int sharecount = 0;
1096 int jn_count = 0;
1098 if(!lp_host_msdfs()) {
1099 return 0;
1102 /* Ensure all the usershares are loaded. */
1103 become_root();
1104 sharecount = load_usershare_shares();
1105 unbecome_root();
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);
1112 return jn_count;