2 * DNS referrals give two main fields: the path to connect to in
3 * /Netbios-host-name/share-name/path... form and a network
4 * address of how to find this path of the form domain.dom.
6 * The domain.dom is resolved in XP/Win2k etc using AD to do
7 * a lookup (this is a consensus view, I don't think anyone
8 * has proved it). I cannot do this as AD needs Kerberos and
9 * LDAP which I don't have.
11 * Instead I just use the NetBios names passed in the paths
12 * and assume that the servers are in the same DNS domain as me
13 * and have their DNS hostname set the same as their netbios
14 * called-name; thankfully this always seems to be the case (so far).
16 * I have not added support for starting another instance of
17 * cifs to connect to other servers referenced in DFS links,
18 * this is not a problem for me and I think it hides a load
19 * of problems of its own wrt plan9's private namespaces.
21 * The expiry of my test server (AD enabled) is always 0 but some
22 * systems may report more meaningful values.
24 * If the redirection points to a "hidden" share (i.e., its name
25 * ends in a $) then the type of the redirection is 0 (unknown) even
26 * though it is a CIFS share.
28 * It would be nice to add a check for which subnet a server is on
29 * so our first choice is always the the server on the same subnet
30 * as us which replies to a ping (i.e., is up). This could short-
31 * circuit the tests as the a server on the same subnet will always
32 * be the fastest to get to.
34 * If I set Flags2_DFS then I don't see DFS links, I just get
35 * path not found (?!).
37 * If I do a QueryFileInfo of a DFS link point (IE when I'am doing a walk)
38 * Then I just see a directory, its not until I try to walk another level
39 * That I get "IO reparse tag not handled" error rather than
42 * If I check the extended attributes of the QueryFileInfo in walk() then I can
43 * see this is a reparse point and so I can get the referral. The only
44 * problem here is that samba and the like may not support this.
56 Nomatch, /* not found in cache */
57 Exactmatch, /* perfect match found */
58 Badmatch /* matched but wrong case */
61 #define SINT_MAX 0x7fffffff
63 typedef struct Dfscache Dfscache;
65 Dfscache*next; /* next entry */
70 long expiry; /* expiry time in sec */
71 long rtt; /* round trip time, nsec */
82 for(cp = Cache; cp; cp = cp->next){
83 ex = cp->expiry - time(nil);
86 fmtprint(f, "%-42s %6ld %8.1f %-16s %-24s %s\n",
87 cp->src, ex, (double)cp->rtt/1000.0L,
88 cp->host, cp->share, cp->path);
97 static char name[128];
99 strncpy(name, s, sizeof(name));
100 name[sizeof(name)-1] = 0;
101 if((p = strrchr(name, '$')) != nil && p[1] == 0)
107 lookup(char *path, int *match)
118 for(cp = Cache; cp; cp = cp->next){
122 if(strncmp(path, cp->src, n) != 0)
124 if(path[n] != 0 && path[n] != '/')
143 static char npath[MAX_DFS_PATH];
146 if((cp = lookup(path, &exact)) != nil){
147 snprint(npath, sizeof npath, "/%s%s%s%s", cp->share,
148 *cp->path? "/": "", cp->path, path + strlen(cp->src));
152 if((p = strchr(path+1, '/')) == nil)
154 if(Debug && strstr(Debug, "dfs") != nil)
155 print("mapfile src=%q => dst=%q\n", opath, p);
160 mapshare(char *path, Share **osp)
166 char *tail[] = { "", "$" };
168 if((cp = lookup(path, nil)) == nil)
171 for(sp = Shares; sp < Shares+Nshares; sp++){
172 s = trimshare(sp->name);
173 if(cistrcmp(cp->share, s) != 0)
175 if(Checkcase && strcmp(cp->share, s) != 0)
177 if(Debug && strstr(Debug, "dfs") != nil)
178 print("mapshare, already connected, src=%q => dst=%q\n", path, sp->name);
183 * Try to autoconnect to share if it is not known. Note even if you
184 * didn't specify any shares and let the system autoconnect you may
185 * not already have the share you need as RAP (which we use) throws
186 * away names > 12 chars long. If we where to use RPC then this block
187 * of code would be less important, though it would still be useful
188 * to catch Shares added since cifs(1) was started.
190 sp = Shares + Nshares;
191 for(i = 0; i < 2; i++){
192 try = smprint("%s%s", cp->share, tail[i]);
193 if(CIFStreeconnect(Sess, Sess->cname, try, sp) == 0){
197 if(Debug && strstr(Debug, "dfs") != nil)
198 print("mapshare connected, src=%q dst=%q\n",
205 if(Debug && strstr(Debug, "dfs") != nil)
206 print("mapshare failed src=%s\n", path);
207 werrstr("not found");
212 * Rtt_tol is the fractional tollerance for RTT comparisons.
213 * If a later (further down the list) host's RTT is less than
214 * 1/Rtt_tol better than my current best then I don't bother
215 * with it. This biases me towards entries at the top of the list
216 * which Active Directory has already chosen for me and prevents
217 * noise in RTTs from pushing me to more distant machines.
220 remap(Dfscache *cp, Refer *re)
233 if(Debug && strstr(Debug, "dfs") != nil)
234 print(" remap %s\n", re->addr);
236 for(p = re->addr; *p; p++)
240 if((n = getfields(re->addr, a, sizeof(a), 0, "/")) < 3){
241 if(Debug && strstr(Debug, "dfs") != nil)
242 print(" remap nfields=%d\n", n);
245 if((rtt = ping(a[Hostname], Dfstout)) == -1){
246 if(Debug && strstr(Debug, "dfs") != nil)
247 print(" remap ping failed\n");
250 if(cp->rtt < rtt && (rtt/labs(rtt-cp->rtt)) < Rtt_tol){
251 if(Debug && strstr(Debug, "dfs") != nil)
252 print(" remap bad ping %ld < %ld && %ld < %d\n",
253 cp->rtt, rtt, (rtt/labs(rtt-cp->rtt)), Rtt_tol);
266 cp->expiry = time(nil)+re->ttl;
267 cp->host = estrdup9p(a[Hostname]);
268 cp->share = estrdup9p(trimshare(a[Sharename]));
269 cp->path = estrdup9p(a[Pathname]);
270 if(Debug && strstr(Debug, "dfs") != nil)
271 print(" remap ping OK host=%s share=%s path=%s\n",
272 cp->host, cp->share, cp->path);
277 redir1(Session *s, char *path, Dfscache *cp, int level)
279 Refer retab[16], *re;
280 int n, gflags, used, found;
284 if((n = T2getdfsreferral(s, &Ipc, path, &gflags, &used, retab,
285 nelem(retab))) == -1)
288 if(! (gflags & DFS_HEADER_ROOT))
292 for(re = retab; re < retab+n; re++){
293 if(Debug && strstr(Debug, "dfs") != nil)
294 print("referal level=%d path=%q addr=%q\n",
295 level, re->path, re->addr);
297 if(*re->path == 0 || *re->addr == 0){
303 if(gflags & DFS_HEADER_STORAGE){
304 if(remap(cp, re) == 0)
307 if(redir1(s, re->addr, cp, level+1) != -1) /* ???? */
314 if(Debug && strstr(Debug, "dfs") != nil)
315 print("referal level=%d path=%q found=%d used=%d\n",
316 level, path, found, used);
323 * We can afford to ignore the used count returned by redir
324 * because of the semantics of 9p - we always walk to directories
325 * ome and we a time and we always walk before any other file operations
328 redirect(Session *s, Share *sp, char *path)
334 if(Debug && strstr(Debug, "dfs") != nil)
335 print("redirect name=%q path=%q\n", sp->name, path);
337 cp = lookup(path, &match);
338 if(match == Badmatch)
341 if(cp && match == Exactmatch){
342 if(cp->expiry >= time(nil)){ /* cache hit */
343 if(Debug && strstr(Debug, "dfs") != nil)
344 print("redirect cache=hit src=%q => share=%q path=%q\n",
345 cp->src, cp->share, cp->path);
348 } else{ /* cache hit, but entry stale */
351 unc = smprint("//%s/%s/%s%s%s", s->auth->windom,
352 cp->share, cp->path, *cp->path? "/": "",
353 path + strlen(cp->src) + 1);
355 sysfatal("no memory: %r");
356 if(redir1(s, unc, cp, 1) == -1){
357 if(Debug && strstr(Debug, "dfs") != nil)
358 print("redirect refresh failed unc=%q\n",
364 if(Debug && strstr(Debug, "dfs") != nil)
365 print("redirect refresh cache=stale src=%q => share=%q path=%q\n",
366 cp->src, cp->share, cp->path);
372 /* in-exact match or complete miss */
374 unc = smprint("//%s/%s/%s%s%s", s->auth->windom, cp->share,
375 cp->path, *cp->path? "/": "", path + strlen(cp->src) + 1);
377 unc = smprint("//%s%s", s->auth->windom, path);
379 sysfatal("no memory: %r");
381 cp = emalloc9p(sizeof(Dfscache));
382 memset(cp, 0, sizeof(Dfscache));
385 if(redir1(s, unc, cp, 1) == -1){
386 if(Debug && strstr(Debug, "dfs") != nil)
387 print("redirect new failed unc=%q\n", unc);
394 cp->src = estrdup9p(path);
397 if(Debug && strstr(Debug, "dfs") != nil)
398 print("redirect cache=miss src=%q => share=%q path=%q\n",
399 cp->src, cp->share, cp->path);