dfs.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. /*
  10. * DNS referrals give two main fields: the path to connect to in
  11. * /Netbios-host-name/share-name/path... form and a network
  12. * address of how to find this path of the form domain.dom.
  13. *
  14. * The domain.dom is resolved in XP/Win2k etc using AD to do
  15. * a lookup (this is a consensus view, I don't think anyone
  16. * has proved it). I cannot do this as AD needs Kerberos and
  17. * LDAP which I don't have.
  18. *
  19. * Instead I just use the NetBios names passed in the paths
  20. * and assume that the servers are in the same DNS domain as me
  21. * and have their DNS hostname set the same as their netbios
  22. * called-name; thankfully this always seems to be the case (so far).
  23. *
  24. * I have not added support for starting another instance of
  25. * cifs to connect to other servers referenced in DFS links,
  26. * this is not a problem for me and I think it hides a load
  27. * of problems of its own wrt plan9's private namespaces.
  28. *
  29. * The proximity of my test server (AD enabled) is always 0 but some
  30. * systems may report more meaningful values. The expiry time is
  31. * similarly zero, so I guess at 5 mins.
  32. *
  33. * If the redirection points to a "hidden" share (i.e., its name
  34. * ends in a $) then the type of the redirection is 0 (unknown) even
  35. * though it is a CIFS share.
  36. *
  37. * It would be nice to add a check for which subnet a server is on
  38. * so our first choice is always the the server on the same subnet
  39. * as us which replies to a ping (i.e., is up). This could short-
  40. * circuit the tests as the a server on the same subnet will always
  41. * be the fastest to get to.
  42. *
  43. * If I set Flags2_DFS then I don't see DFS links, I just get
  44. * path not found (?!).
  45. *
  46. * If I do a QueryFileInfo of a DFS link point (IE when I'am doing a walk)
  47. * Then I just see a directory, its not until I try to walk another level
  48. * That I get "IO reparse tag not handled" error rather than
  49. * "Path not covered".
  50. *
  51. * If I check the extended attributes of the QueryFileInfo in walk() then I can
  52. * see this is a reparse point and so I can get the referral. The only
  53. * problem here is that samba and the like may not support this.
  54. */
  55. #include <u.h>
  56. #include <libc.h>
  57. #include <fcall.h>
  58. #include <thread.h>
  59. #include <libsec.h>
  60. #include <ctype.h>
  61. #include <9p.h>
  62. #include "cifs.h"
  63. enum {
  64. Nomatch, /* not found in cache */
  65. Exactmatch, /* perfect match found */
  66. Badmatch /* matched but wrong case */
  67. };
  68. #define SINT_MAX 0x7fffffff
  69. typedef struct Dfscache Dfscache;
  70. struct Dfscache {
  71. Dfscache*next; /* next entry */
  72. char *src;
  73. char *host;
  74. char *share;
  75. char *path;
  76. int32_t expiry; /* expiry time in sec */
  77. int32_t rtt; /* round trip time, nsec */
  78. int prox; /* proximity, lower = closer */
  79. };
  80. Dfscache *Cache;
  81. int
  82. dfscacheinfo(Fmt *f)
  83. {
  84. int32_t ex;
  85. Dfscache *cp;
  86. for(cp = Cache; cp; cp = cp->next){
  87. ex = cp->expiry - time(nil);
  88. if(ex < 0)
  89. ex = -1;
  90. fmtprint(f, "%-42s %6ld %8.1f %4d %-16s %-24s %s\n",
  91. cp->src, ex, (double)cp->rtt/1000.0L, cp->prox,
  92. cp->host, cp->share, cp->path);
  93. }
  94. return 0;
  95. }
  96. char *
  97. trimshare(char *s)
  98. {
  99. char *p;
  100. static char name[128];
  101. strncpy(name, s, sizeof(name));
  102. name[sizeof(name)-1] = 0;
  103. if((p = strrchr(name, '$')) != nil && p[1] == 0)
  104. *p = 0;
  105. return name;
  106. }
  107. static Dfscache *
  108. lookup(char *path, int *match)
  109. {
  110. int len, n, m;
  111. Dfscache *cp, *best;
  112. if(match)
  113. *match = Nomatch;
  114. len = 0;
  115. best = nil;
  116. m = strlen(path);
  117. for(cp = Cache; cp; cp = cp->next){
  118. n = strlen(cp->src);
  119. if(n < len)
  120. continue;
  121. if(strncmp(path, cp->src, n) != 0)
  122. continue;
  123. if(path[n] != 0 && path[n] != '/')
  124. continue;
  125. best = cp;
  126. len = n;
  127. if(n == m){
  128. if(match)
  129. *match = Exactmatch;
  130. break;
  131. }
  132. }
  133. return best;
  134. }
  135. char *
  136. mapfile(char *opath)
  137. {
  138. int exact;
  139. Dfscache *cp;
  140. char *p, *path;
  141. static char npath[MAX_DFS_PATH];
  142. path = opath;
  143. if((cp = lookup(path, &exact)) != nil){
  144. snprint(npath, sizeof npath, "/%s%s%s%s", cp->share,
  145. *cp->path? "/": "", cp->path, path + strlen(cp->src));
  146. path = npath;
  147. }
  148. if((p = strchr(path+1, '/')) == nil)
  149. p = "/";
  150. if(Debug && strstr(Debug, "dfs") != nil)
  151. print("mapfile src=%q => dst=%q\n", opath, p);
  152. return p;
  153. }
  154. int
  155. mapshare(char *path, Share **osp)
  156. {
  157. int i;
  158. Share *sp;
  159. Dfscache *cp;
  160. char *s, *try;
  161. char *tail[] = { "", "$" };
  162. if((cp = lookup(path, nil)) == nil)
  163. return 0;
  164. for(sp = Shares; sp < Shares+Nshares; sp++){
  165. s = trimshare(sp->name);
  166. if(cistrcmp(cp->share, s) != 0)
  167. continue;
  168. if(Checkcase && strcmp(cp->share, s) != 0)
  169. continue;
  170. if(Debug && strstr(Debug, "dfs") != nil)
  171. print("mapshare, already connected, src=%q => dst=%q\n", path, sp->name);
  172. *osp = sp;
  173. return 0;
  174. }
  175. /*
  176. * Try to autoconnect to share if it is not known. Note even if you
  177. * didn't specify any shares and let the system autoconnect you may
  178. * not already have the share you need as RAP (which we use) throws
  179. * away names > 12 chars long. If we where to use RPC then this block
  180. * of code would be less important, though it would still be useful
  181. * to catch Shares added since cifs(1) was started.
  182. */
  183. sp = Shares + Nshares;
  184. for(i = 0; i < 2; i++){
  185. try = smprint("%s%s", cp->share, tail[i]);
  186. if(CIFStreeconnect(Sess, Sess->cname, try, sp) == 0){
  187. sp->name = try;
  188. *osp = sp;
  189. Nshares++;
  190. if(Debug && strstr(Debug, "dfs") != nil)
  191. print("mapshare connected, src=%q dst=%q\n",
  192. path, cp->share);
  193. return 0;
  194. }
  195. free(try);
  196. }
  197. if(Debug && strstr(Debug, "dfs") != nil)
  198. print("mapshare failed src=%s\n", path);
  199. werrstr("not found");
  200. return -1;
  201. }
  202. /*
  203. * Rtt_tol is the fractional tollerance for RTT comparisons.
  204. * If a later (further down the list) host's RTT is less than
  205. * 1/Rtt_tol better than my current best then I don't bother
  206. * with it. This biases me towards entries at the top of the list
  207. * which Active Directory has already chosen for me and prevents
  208. * noise in RTTs from pushing me to more distant machines.
  209. */
  210. static int
  211. remap(Dfscache *cp, Refer *re)
  212. {
  213. int n;
  214. int32_t rtt;
  215. char *p, *a[4];
  216. enum {
  217. Hostname = 1,
  218. Sharename = 2,
  219. Pathname = 3,
  220. Rtt_tol = 10
  221. };
  222. if(Debug && strstr(Debug, "dfs") != nil)
  223. print(" remap %s\n", re->addr);
  224. for(p = re->addr; *p; p++)
  225. if(*p == '\\')
  226. *p = '/';
  227. if(cp->prox < re->prox){
  228. if(Debug && strstr(Debug, "dfs") != nil)
  229. print(" remap %d < %d\n", cp->prox, re->prox);
  230. return -1;
  231. }
  232. if((n = getfields(re->addr, a, sizeof(a), 0, "/")) < 3){
  233. if(Debug && strstr(Debug, "dfs") != nil)
  234. print(" remap nfields=%d\n", n);
  235. return -1;
  236. }
  237. if((rtt = ping(a[Hostname], Dfstout)) == -1){
  238. if(Debug && strstr(Debug, "dfs") != nil)
  239. print(" remap ping failed\n");
  240. return -1;
  241. }
  242. if(cp->rtt < rtt && (rtt/labs(rtt-cp->rtt)) < Rtt_tol){
  243. if(Debug && strstr(Debug, "dfs") != nil)
  244. print(" remap bad ping %ld < %ld && %ld < %d\n",
  245. cp->rtt, rtt, (rtt/labs(rtt-cp->rtt)), Rtt_tol);
  246. return -1;
  247. }
  248. if(n < 4)
  249. a[Pathname] = "";
  250. if(re->ttl == 0)
  251. re->ttl = 60*5;
  252. free(cp->host);
  253. free(cp->share);
  254. free(cp->path);
  255. cp->rtt = rtt;
  256. cp->prox = re->prox;
  257. cp->expiry = time(nil)+re->ttl;
  258. cp->host = estrdup9p(a[Hostname]);
  259. cp->share = estrdup9p(trimshare(a[Sharename]));
  260. cp->path = estrdup9p(a[Pathname]);
  261. if(Debug && strstr(Debug, "dfs") != nil)
  262. print(" remap ping OK prox=%d host=%s share=%s path=%s\n",
  263. cp->prox, cp->host, cp->share, cp->path);
  264. return 0;
  265. }
  266. static int
  267. redir1(Session *s, char *path, Dfscache *cp, int level)
  268. {
  269. Refer retab[16], *re;
  270. int n, gflags, used, found;
  271. if(level > 8)
  272. return -1;
  273. if((n = T2getdfsreferral(s, &Ipc, path, &gflags, &used, retab,
  274. nelem(retab))) == -1)
  275. return -1;
  276. if(! (gflags & DFS_HEADER_ROOT))
  277. used = SINT_MAX;
  278. found = 0;
  279. for(re = retab; re < retab+n; re++){
  280. if(Debug && strstr(Debug, "dfs") != nil)
  281. print("referal level=%d prox=%d path=%q addr=%q\n",
  282. level, re->prox, re->path, re->addr);
  283. if(gflags & DFS_HEADER_STORAGE){
  284. if(remap(cp, re) == 0)
  285. found = 1;
  286. } else{
  287. if(redir1(s, re->addr, cp, level+1) != -1) /* ???? */
  288. found = 1;
  289. }
  290. free(re->addr);
  291. free(re->path);
  292. }
  293. if(Debug && strstr(Debug, "dfs") != nil)
  294. print("referal level=%d path=%q found=%d used=%d\n",
  295. level, path, found, used);
  296. if(!found)
  297. return -1;
  298. return used;
  299. }
  300. /*
  301. * We can afford to ignore the used count returned by redir
  302. * because of the semantics of 9p - we always walk to directories
  303. * ome and we a time and we always walk before any other file operations
  304. */
  305. int
  306. redirect(Session *s, Share *sp, char *path)
  307. {
  308. int match;
  309. char *unc;
  310. Dfscache *cp;
  311. if(Debug && strstr(Debug, "dfs") != nil)
  312. print("redirect name=%q path=%q\n", sp->name, path);
  313. cp = lookup(path, &match);
  314. if(match == Badmatch)
  315. return -1;
  316. if(cp && match == Exactmatch){
  317. if(cp->expiry >= time(nil)){ /* cache hit */
  318. if(Debug && strstr(Debug, "dfs") != nil)
  319. print("redirect cache=hit src=%q => share=%q path=%q\n",
  320. cp->src, cp->share, cp->path);
  321. return 0;
  322. } else{ /* cache hit, but entry stale */
  323. cp->rtt = SINT_MAX;
  324. cp->prox = SINT_MAX;
  325. unc = smprint("//%s/%s/%s%s%s", s->auth->windom,
  326. cp->share, cp->path, *cp->path? "/": "",
  327. path + strlen(cp->src) + 1);
  328. if(unc == nil)
  329. sysfatal("no memory: %r");
  330. if(redir1(s, unc, cp, 1) == -1){
  331. if(Debug && strstr(Debug, "dfs") != nil)
  332. print("redirect refresh failed unc=%q\n",
  333. unc);
  334. free(unc);
  335. return -1;
  336. }
  337. free(unc);
  338. if(Debug && strstr(Debug, "dfs") != nil)
  339. print("redirect refresh cache=stale src=%q => share=%q path=%q\n",
  340. cp->src, cp->share, cp->path);
  341. return 0;
  342. }
  343. }
  344. /* in-exact match or complete miss */
  345. if(cp)
  346. unc = smprint("//%s/%s/%s%s%s", s->auth->windom, cp->share,
  347. cp->path, *cp->path? "/": "", path + strlen(cp->src) + 1);
  348. else
  349. unc = smprint("//%s%s", s->auth->windom, path);
  350. if(unc == nil)
  351. sysfatal("no memory: %r");
  352. cp = emalloc9p(sizeof(Dfscache));
  353. memset(cp, 0, sizeof(Dfscache));
  354. cp->rtt = SINT_MAX;
  355. cp->prox = SINT_MAX;
  356. if(redir1(s, unc, cp, 1) == -1){
  357. if(Debug && strstr(Debug, "dfs") != nil)
  358. print("redirect new failed unc=%q\n", unc);
  359. free(unc);
  360. free(cp);
  361. return -1;
  362. }
  363. free(unc);
  364. cp->src = estrdup9p(path);
  365. cp->next = Cache;
  366. Cache = cp;
  367. if(Debug && strstr(Debug, "dfs") != nil)
  368. print("redirect cache=miss src=%q => share=%q path=%q\n",
  369. cp->src, cp->share, cp->path);
  370. return 0;
  371. }