dfs.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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 <mp.h>
  60. #include <libsec.h>
  61. #include <ctype.h>
  62. #include <9p.h>
  63. #include "cifs.h"
  64. enum {
  65. Nomatch, /* not found in cache */
  66. Exactmatch, /* perfect match found */
  67. Badmatch /* matched but wrong case */
  68. };
  69. #define SINT_MAX 0x7fffffff
  70. typedef struct Dfscache Dfscache;
  71. struct Dfscache {
  72. Dfscache*next; /* next entry */
  73. char *src;
  74. char *host;
  75. char *share;
  76. char *path;
  77. int32_t expiry; /* expiry time in sec */
  78. int32_t rtt; /* round trip time, nsec */
  79. int prox; /* proximity, lower = closer */
  80. };
  81. Dfscache *Cache;
  82. int
  83. dfscacheinfo(Fmt *f)
  84. {
  85. int32_t ex;
  86. Dfscache *cp;
  87. for(cp = Cache; cp; cp = cp->next){
  88. ex = cp->expiry - time(nil);
  89. if(ex < 0)
  90. ex = -1;
  91. fmtprint(f, "%-42s %6ld %8.1f %4d %-16s %-24s %s\n",
  92. cp->src, ex, (double)cp->rtt/1000.0L, cp->prox,
  93. cp->host, cp->share, cp->path);
  94. }
  95. return 0;
  96. }
  97. char *
  98. trimshare(char *s)
  99. {
  100. char *p;
  101. static char name[128];
  102. strncpy(name, s, sizeof(name));
  103. name[sizeof(name)-1] = 0;
  104. if((p = strrchr(name, '$')) != nil && p[1] == 0)
  105. *p = 0;
  106. return name;
  107. }
  108. static Dfscache *
  109. lookup(char *path, int *match)
  110. {
  111. int len, n, m;
  112. Dfscache *cp, *best;
  113. if(match)
  114. *match = Nomatch;
  115. len = 0;
  116. best = nil;
  117. m = strlen(path);
  118. for(cp = Cache; cp; cp = cp->next){
  119. n = strlen(cp->src);
  120. if(n < len)
  121. continue;
  122. if(strncmp(path, cp->src, n) != 0)
  123. continue;
  124. if(path[n] != 0 && path[n] != '/')
  125. continue;
  126. best = cp;
  127. len = n;
  128. if(n == m){
  129. if(match)
  130. *match = Exactmatch;
  131. break;
  132. }
  133. }
  134. return best;
  135. }
  136. char *
  137. mapfile(char *opath)
  138. {
  139. int exact;
  140. Dfscache *cp;
  141. char *p, *path;
  142. static char npath[MAX_DFS_PATH];
  143. path = opath;
  144. if((cp = lookup(path, &exact)) != nil){
  145. snprint(npath, sizeof npath, "/%s%s%s%s", cp->share,
  146. *cp->path? "/": "", cp->path, path + strlen(cp->src));
  147. path = npath;
  148. }
  149. if((p = strchr(path+1, '/')) == nil)
  150. p = "/";
  151. if(Debug && strstr(Debug, "dfs") != nil)
  152. print("mapfile src=%q => dst=%q\n", opath, p);
  153. return p;
  154. }
  155. int
  156. mapshare(char *path, Share **osp)
  157. {
  158. int i;
  159. Share *sp;
  160. Dfscache *cp;
  161. char *s, *try;
  162. char *tail[] = { "", "$" };
  163. if((cp = lookup(path, nil)) == nil)
  164. return 0;
  165. for(sp = Shares; sp < Shares+Nshares; sp++){
  166. s = trimshare(sp->name);
  167. if(cistrcmp(cp->share, s) != 0)
  168. continue;
  169. if(Checkcase && strcmp(cp->share, s) != 0)
  170. continue;
  171. if(Debug && strstr(Debug, "dfs") != nil)
  172. print("mapshare, already connected, src=%q => dst=%q\n", path, sp->name);
  173. *osp = sp;
  174. return 0;
  175. }
  176. /*
  177. * Try to autoconnect to share if it is not known. Note even if you
  178. * didn't specify any shares and let the system autoconnect you may
  179. * not already have the share you need as RAP (which we use) throws
  180. * away names > 12 chars long. If we where to use RPC then this block
  181. * of code would be less important, though it would still be useful
  182. * to catch Shares added since cifs(1) was started.
  183. */
  184. sp = Shares + Nshares;
  185. for(i = 0; i < 2; i++){
  186. try = smprint("%s%s", cp->share, tail[i]);
  187. if(CIFStreeconnect(Sess, Sess->cname, try, sp) == 0){
  188. sp->name = try;
  189. *osp = sp;
  190. Nshares++;
  191. if(Debug && strstr(Debug, "dfs") != nil)
  192. print("mapshare connected, src=%q dst=%q\n",
  193. path, cp->share);
  194. return 0;
  195. }
  196. free(try);
  197. }
  198. if(Debug && strstr(Debug, "dfs") != nil)
  199. print("mapshare failed src=%s\n", path);
  200. werrstr("not found");
  201. return -1;
  202. }
  203. /*
  204. * Rtt_tol is the fractional tollerance for RTT comparisons.
  205. * If a later (further down the list) host's RTT is less than
  206. * 1/Rtt_tol better than my current best then I don't bother
  207. * with it. This biases me towards entries at the top of the list
  208. * which Active Directory has already chosen for me and prevents
  209. * noise in RTTs from pushing me to more distant machines.
  210. */
  211. static int
  212. remap(Dfscache *cp, Refer *re)
  213. {
  214. int n;
  215. int32_t rtt;
  216. char *p, *a[4];
  217. enum {
  218. Hostname = 1,
  219. Sharename = 2,
  220. Pathname = 3,
  221. Rtt_tol = 10
  222. };
  223. if(Debug && strstr(Debug, "dfs") != nil)
  224. print(" remap %s\n", re->addr);
  225. for(p = re->addr; *p; p++)
  226. if(*p == '\\')
  227. *p = '/';
  228. if(cp->prox < re->prox){
  229. if(Debug && strstr(Debug, "dfs") != nil)
  230. print(" remap %d < %d\n", cp->prox, re->prox);
  231. return -1;
  232. }
  233. if((n = getfields(re->addr, a, sizeof(a), 0, "/")) < 3){
  234. if(Debug && strstr(Debug, "dfs") != nil)
  235. print(" remap nfields=%d\n", n);
  236. return -1;
  237. }
  238. if((rtt = ping(a[Hostname], Dfstout)) == -1){
  239. if(Debug && strstr(Debug, "dfs") != nil)
  240. print(" remap ping failed\n");
  241. return -1;
  242. }
  243. if(cp->rtt < rtt && (rtt/labs(rtt-cp->rtt)) < Rtt_tol){
  244. if(Debug && strstr(Debug, "dfs") != nil)
  245. print(" remap bad ping %ld < %ld && %ld < %d\n",
  246. cp->rtt, rtt, (rtt/labs(rtt-cp->rtt)), Rtt_tol);
  247. return -1;
  248. }
  249. if(n < 4)
  250. a[Pathname] = "";
  251. if(re->ttl == 0)
  252. re->ttl = 60*5;
  253. free(cp->host);
  254. free(cp->share);
  255. free(cp->path);
  256. cp->rtt = rtt;
  257. cp->prox = re->prox;
  258. cp->expiry = time(nil)+re->ttl;
  259. cp->host = estrdup9p(a[Hostname]);
  260. cp->share = estrdup9p(trimshare(a[Sharename]));
  261. cp->path = estrdup9p(a[Pathname]);
  262. if(Debug && strstr(Debug, "dfs") != nil)
  263. print(" remap ping OK prox=%d host=%s share=%s path=%s\n",
  264. cp->prox, cp->host, cp->share, cp->path);
  265. return 0;
  266. }
  267. static int
  268. redir1(Session *s, char *path, Dfscache *cp, int level)
  269. {
  270. Refer retab[16], *re;
  271. int n, gflags, used, found;
  272. if(level > 8)
  273. return -1;
  274. if((n = T2getdfsreferral(s, &Ipc, path, &gflags, &used, retab,
  275. nelem(retab))) == -1)
  276. return -1;
  277. if(! (gflags & DFS_HEADER_ROOT))
  278. used = SINT_MAX;
  279. found = 0;
  280. for(re = retab; re < retab+n; re++){
  281. if(Debug && strstr(Debug, "dfs") != nil)
  282. print("referal level=%d prox=%d path=%q addr=%q\n",
  283. level, re->prox, re->path, re->addr);
  284. if(gflags & DFS_HEADER_STORAGE){
  285. if(remap(cp, re) == 0)
  286. found = 1;
  287. } else{
  288. if(redir1(s, re->addr, cp, level+1) != -1) /* ???? */
  289. found = 1;
  290. }
  291. free(re->addr);
  292. free(re->path);
  293. }
  294. if(Debug && strstr(Debug, "dfs") != nil)
  295. print("referal level=%d path=%q found=%d used=%d\n",
  296. level, path, found, used);
  297. if(!found)
  298. return -1;
  299. return used;
  300. }
  301. /*
  302. * We can afford to ignore the used count returned by redir
  303. * because of the semantics of 9p - we always walk to directories
  304. * ome and we a time and we always walk before any other file operations
  305. */
  306. int
  307. redirect(Session *s, Share *sp, char *path)
  308. {
  309. int match;
  310. char *unc;
  311. Dfscache *cp;
  312. if(Debug && strstr(Debug, "dfs") != nil)
  313. print("redirect name=%q path=%q\n", sp->name, path);
  314. cp = lookup(path, &match);
  315. if(match == Badmatch)
  316. return -1;
  317. if(cp && match == Exactmatch){
  318. if(cp->expiry >= time(nil)){ /* cache hit */
  319. if(Debug && strstr(Debug, "dfs") != nil)
  320. print("redirect cache=hit src=%q => share=%q path=%q\n",
  321. cp->src, cp->share, cp->path);
  322. return 0;
  323. } else{ /* cache hit, but entry stale */
  324. cp->rtt = SINT_MAX;
  325. cp->prox = SINT_MAX;
  326. unc = smprint("//%s/%s/%s%s%s", s->auth->windom,
  327. cp->share, cp->path, *cp->path? "/": "",
  328. path + strlen(cp->src) + 1);
  329. if(unc == nil)
  330. sysfatal("no memory: %r");
  331. if(redir1(s, unc, cp, 1) == -1){
  332. if(Debug && strstr(Debug, "dfs") != nil)
  333. print("redirect refresh failed unc=%q\n",
  334. unc);
  335. free(unc);
  336. return -1;
  337. }
  338. free(unc);
  339. if(Debug && strstr(Debug, "dfs") != nil)
  340. print("redirect refresh cache=stale src=%q => share=%q path=%q\n",
  341. cp->src, cp->share, cp->path);
  342. return 0;
  343. }
  344. }
  345. /* in-exact match or complete miss */
  346. if(cp)
  347. unc = smprint("//%s/%s/%s%s%s", s->auth->windom, cp->share,
  348. cp->path, *cp->path? "/": "", path + strlen(cp->src) + 1);
  349. else
  350. unc = smprint("//%s%s", s->auth->windom, path);
  351. if(unc == nil)
  352. sysfatal("no memory: %r");
  353. cp = emalloc9p(sizeof(Dfscache));
  354. memset(cp, 0, sizeof(Dfscache));
  355. cp->rtt = SINT_MAX;
  356. cp->prox = SINT_MAX;
  357. if(redir1(s, unc, cp, 1) == -1){
  358. if(Debug && strstr(Debug, "dfs") != nil)
  359. print("redirect new failed unc=%q\n", unc);
  360. free(unc);
  361. free(cp);
  362. return -1;
  363. }
  364. free(unc);
  365. cp->src = estrdup9p(path);
  366. cp->next = Cache;
  367. Cache = cp;
  368. if(Debug && strstr(Debug, "dfs") != nil)
  369. print("redirect cache=miss src=%q => share=%q path=%q\n",
  370. cp->src, cp->share, cp->path);
  371. return 0;
  372. }