nis_call.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. /* Copyright (C) 1997-2026 Free Software Foundation, Inc.
  2. This file is part of the GNU C Library.
  3. The GNU C Library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. The GNU C Library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with the GNU C Library; if not, see
  13. <https://www.gnu.org/licenses/>. */
  14. #include <errno.h>
  15. #include <fcntl.h>
  16. #include <string.h>
  17. #include <libintl.h>
  18. #include <rpc/rpc.h>
  19. #include <rpc/auth.h>
  20. #include <rpcsvc/nis.h>
  21. #include <sys/socket.h>
  22. #include <sys/stat.h>
  23. #include <unistd.h>
  24. #include <netinet/in.h>
  25. #include <arpa/inet.h>
  26. #include <libc-lock.h>
  27. #include "nis_xdr.h"
  28. #include "nis_intern.h"
  29. #include <libnsl.h>
  30. #include <shlib-compat.h>
  31. static const struct timeval RPCTIMEOUT = {10, 0};
  32. static const struct timeval UDPTIMEOUT = {5, 0};
  33. extern u_short __pmap_getnisport (struct sockaddr_in *address, u_long program,
  34. u_long version, u_int protocol);
  35. unsigned long int
  36. inetstr2int (const char *str)
  37. {
  38. size_t j = 0;
  39. for (size_t i = 0; str[i] != '\0'; ++i)
  40. if (str[i] == '.' && __builtin_expect (++j == 4, 0))
  41. {
  42. char buffer[i + 1];
  43. buffer[i] = '\0';
  44. return inet_addr (memcpy (buffer, str, i));
  45. }
  46. return inet_addr (str);
  47. }
  48. void
  49. __nisbind_destroy (dir_binding *bind)
  50. {
  51. if (bind->clnt != NULL)
  52. {
  53. if (bind->use_auth)
  54. auth_destroy (bind->clnt->cl_auth);
  55. clnt_destroy (bind->clnt);
  56. }
  57. }
  58. libnsl_hidden_nolink_def (__nisbind_destroy, GLIBC_2_1)
  59. nis_error
  60. __nisbind_next (dir_binding *bind)
  61. {
  62. if (bind->clnt != NULL)
  63. {
  64. if (bind->use_auth)
  65. auth_destroy (bind->clnt->cl_auth);
  66. clnt_destroy (bind->clnt);
  67. bind->clnt = NULL;
  68. }
  69. if (bind->trys >= bind->server_len)
  70. return NIS_FAIL;
  71. for (u_int j = bind->current_ep + 1;
  72. j < bind->server_val[bind->server_used].ep.ep_len; ++j)
  73. if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
  74. "inet") == 0)
  75. if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
  76. {
  77. bind->current_ep = j;
  78. return NIS_SUCCESS;
  79. }
  80. ++bind->trys;
  81. ++bind->server_used;
  82. if (bind->server_used >= bind->server_len)
  83. bind->server_used = 0;
  84. for (u_int j = 0; j < bind->server_val[bind->server_used].ep.ep_len; ++j)
  85. if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
  86. "inet") == 0)
  87. if (bind->server_val[bind->server_used].ep.ep_val[j].proto[0] == '-')
  88. {
  89. bind->current_ep = j;
  90. return NIS_SUCCESS;
  91. }
  92. return NIS_FAIL;
  93. }
  94. libnsl_hidden_nolink_def (__nisbind_next, GLIBC_2_1)
  95. static struct ckey_cache_entry
  96. {
  97. struct in_addr inaddr;
  98. in_port_t port;
  99. unsigned int protocol;
  100. des_block ckey;
  101. } *ckey_cache;
  102. static size_t ckey_cache_size;
  103. static size_t ckey_cache_allocated;
  104. static pid_t ckey_cache_pid;
  105. static uid_t ckey_cache_euid;
  106. __libc_lock_define_initialized (static, ckey_cache_lock)
  107. static bool_t
  108. get_ckey (des_block *ckey, struct sockaddr_in *addr, unsigned int protocol)
  109. {
  110. size_t i;
  111. pid_t pid = getpid ();
  112. uid_t euid = geteuid ();
  113. bool_t ret = FALSE;
  114. __libc_lock_lock (ckey_cache_lock);
  115. if (ckey_cache_pid != pid || ckey_cache_euid != euid)
  116. {
  117. ckey_cache_size = 0;
  118. ckey_cache_pid = pid;
  119. ckey_cache_euid = euid;
  120. }
  121. for (i = 0; i < ckey_cache_size; ++i)
  122. if (ckey_cache[i].port == addr->sin_port
  123. && ckey_cache[i].protocol == protocol
  124. && memcmp (&ckey_cache[i].inaddr, &addr->sin_addr,
  125. sizeof (addr->sin_addr)) == 0)
  126. {
  127. *ckey = ckey_cache[i].ckey;
  128. ret = TRUE;
  129. break;
  130. }
  131. if (!ret && key_gendes (ckey) >= 0)
  132. {
  133. ret = TRUE;
  134. /* Don't grow the cache indefinitely. */
  135. if (ckey_cache_size == 256)
  136. ckey_cache_size = 0;
  137. if (ckey_cache_size == ckey_cache_allocated)
  138. {
  139. size_t size = ckey_cache_allocated ? ckey_cache_allocated * 2 : 16;
  140. struct ckey_cache_entry *new_cache
  141. = realloc (ckey_cache, size * sizeof (*ckey_cache));
  142. if (new_cache != NULL)
  143. {
  144. ckey_cache = new_cache;
  145. ckey_cache_allocated = size;
  146. }
  147. }
  148. ckey_cache[ckey_cache_size].inaddr = addr->sin_addr;
  149. ckey_cache[ckey_cache_size].port = addr->sin_port;
  150. ckey_cache[ckey_cache_size].protocol = protocol;
  151. ckey_cache[ckey_cache_size++].ckey = *ckey;
  152. }
  153. __libc_lock_unlock (ckey_cache_lock);
  154. return ret;
  155. }
  156. nis_error
  157. __nisbind_connect (dir_binding *dbp)
  158. {
  159. nis_server *serv;
  160. u_short port;
  161. if (dbp == NULL)
  162. return NIS_FAIL;
  163. serv = &dbp->server_val[dbp->server_used];
  164. memset (&dbp->addr, '\0', sizeof (dbp->addr));
  165. dbp->addr.sin_family = AF_INET;
  166. dbp->addr.sin_addr.s_addr =
  167. inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
  168. if (dbp->addr.sin_addr.s_addr == INADDR_NONE)
  169. return NIS_FAIL;
  170. /* Check, if the host is online and rpc.nisd is running. Much faster
  171. then the clnt*_create functions: */
  172. port = __pmap_getnisport (&dbp->addr, NIS_PROG, NIS_VERSION,
  173. dbp->use_udp ? IPPROTO_UDP : IPPROTO_TCP);
  174. if (port == 0)
  175. return NIS_RPCERROR;
  176. dbp->addr.sin_port = htons (port);
  177. dbp->socket = RPC_ANYSOCK;
  178. if (dbp->use_udp)
  179. dbp->clnt = clntudp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
  180. UDPTIMEOUT, &dbp->socket);
  181. else
  182. dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
  183. &dbp->socket, 0, 0);
  184. if (dbp->clnt == NULL)
  185. return NIS_RPCERROR;
  186. clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t) &RPCTIMEOUT);
  187. /* If the program exists, close the socket */
  188. if (fcntl (dbp->socket, F_SETFD, 1) == -1)
  189. perror ("fcntl: F_SETFD");
  190. if (dbp->use_auth)
  191. {
  192. if (serv->key_type == NIS_PK_DH)
  193. {
  194. char netname[MAXNETNAMELEN + 1];
  195. char *p;
  196. des_block ckey;
  197. p = stpcpy (netname, "unix@");
  198. strncpy (p, serv->name, MAXNETNAMELEN - 5);
  199. netname[MAXNETNAMELEN] = '\0';
  200. dbp->clnt->cl_auth = NULL;
  201. if (get_ckey (&ckey, &dbp->addr,
  202. dbp->use_udp ? IPPROTO_UDP : IPPROTO_TCP))
  203. dbp->clnt->cl_auth =
  204. authdes_pk_create (netname, &serv->pkey, 300, NULL, &ckey);
  205. if (!dbp->clnt->cl_auth)
  206. dbp->clnt->cl_auth = authunix_create_default ();
  207. }
  208. else
  209. dbp->clnt->cl_auth = authunix_create_default ();
  210. }
  211. return NIS_SUCCESS;
  212. }
  213. libnsl_hidden_nolink_def (__nisbind_connect, GLIBC_2_1)
  214. nis_error
  215. __nisbind_create (dir_binding *dbp, const nis_server *serv_val,
  216. unsigned int serv_len, unsigned int server_used,
  217. unsigned int current_ep, unsigned int flags)
  218. {
  219. dbp->clnt = NULL;
  220. dbp->server_len = serv_len;
  221. dbp->server_val = (nis_server *)serv_val;
  222. if (flags & USE_DGRAM)
  223. dbp->use_udp = TRUE;
  224. else
  225. dbp->use_udp = FALSE;
  226. if (flags & NO_AUTHINFO)
  227. dbp->use_auth = FALSE;
  228. else
  229. dbp->use_auth = TRUE;
  230. if (flags & MASTER_ONLY)
  231. dbp->master_only = TRUE;
  232. else
  233. dbp->master_only = FALSE;
  234. /* We try the first server */
  235. dbp->trys = 1;
  236. dbp->class = -1;
  237. if (server_used == ~0)
  238. {
  239. if (__nis_findfastest (dbp) < 1)
  240. return NIS_NAMEUNREACHABLE;
  241. }
  242. else
  243. {
  244. dbp->server_used = server_used;
  245. dbp->current_ep = current_ep;
  246. }
  247. return NIS_SUCCESS;
  248. }
  249. libnsl_hidden_nolink_def (__nisbind_create, GLIBC_2_1)
  250. /* __nisbind_connect (dbp) must be run before calling this function !
  251. So we could use the same binding twice */
  252. nis_error
  253. __do_niscall3 (dir_binding *dbp, u_long prog, xdrproc_t xargs, caddr_t req,
  254. xdrproc_t xres, caddr_t resp, unsigned int flags, nis_cb *cb)
  255. {
  256. enum clnt_stat result;
  257. nis_error retcode;
  258. if (dbp == NULL)
  259. return NIS_NAMEUNREACHABLE;
  260. do
  261. {
  262. again:
  263. result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, RPCTIMEOUT);
  264. if (result != RPC_SUCCESS)
  265. retcode = NIS_RPCERROR;
  266. else
  267. {
  268. switch (prog)
  269. {
  270. case NIS_IBLIST:
  271. if ((((nis_result *)resp)->status == NIS_CBRESULTS)
  272. && (cb != NULL))
  273. {
  274. __nis_do_callback (dbp, &((nis_result *) resp)->cookie, cb);
  275. break;
  276. }
  277. /* Yes, the missing break is correct. If we doesn't have to
  278. start a callback, look if we have to search another server */
  279. [[fallthrough]];
  280. case NIS_LOOKUP:
  281. case NIS_ADD:
  282. case NIS_MODIFY:
  283. case NIS_REMOVE:
  284. case NIS_IBADD:
  285. case NIS_IBMODIFY:
  286. case NIS_IBREMOVE:
  287. case NIS_IBFIRST:
  288. case NIS_IBNEXT:
  289. if (((nis_result *)resp)->status == NIS_SYSTEMERROR
  290. || ((nis_result *)resp)->status == NIS_NOSUCHNAME
  291. || ((nis_result *)resp)->status == NIS_NOT_ME)
  292. {
  293. next_server:
  294. if (__nisbind_next (dbp) == NIS_SUCCESS)
  295. {
  296. while (__nisbind_connect (dbp) != NIS_SUCCESS)
  297. {
  298. if (__nisbind_next (dbp) != NIS_SUCCESS)
  299. return NIS_SUCCESS;
  300. }
  301. }
  302. else
  303. break; /* No more servers to search in */
  304. goto again;
  305. }
  306. break;
  307. case NIS_FINDDIRECTORY:
  308. if (((fd_result *)resp)->status == NIS_SYSTEMERROR
  309. || ((fd_result *)resp)->status == NIS_NOSUCHNAME
  310. || ((fd_result *)resp)->status == NIS_NOT_ME)
  311. goto next_server;
  312. break;
  313. case NIS_DUMPLOG: /* log_result */
  314. case NIS_DUMP:
  315. if (((log_result *)resp)->lr_status == NIS_SYSTEMERROR
  316. || ((log_result *)resp)->lr_status == NIS_NOSUCHNAME
  317. || ((log_result *)resp)->lr_status == NIS_NOT_ME)
  318. goto next_server;
  319. break;
  320. default:
  321. break;
  322. }
  323. retcode = NIS_SUCCESS;
  324. }
  325. }
  326. while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
  327. return retcode;
  328. }
  329. libnsl_hidden_nolink_def (__do_niscall3, GLIBC_PRIVATE)
  330. nis_error
  331. __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
  332. xdrproc_t xargs, caddr_t req, xdrproc_t xres, caddr_t resp,
  333. unsigned int flags, nis_cb *cb)
  334. {
  335. dir_binding dbp;
  336. nis_error status;
  337. if (flags & MASTER_ONLY)
  338. server_len = 1;
  339. status = __nisbind_create (&dbp, server, server_len, ~0, ~0, flags);
  340. if (status != NIS_SUCCESS)
  341. return status;
  342. while (__nisbind_connect (&dbp) != NIS_SUCCESS)
  343. if (__nisbind_next (&dbp) != NIS_SUCCESS)
  344. return NIS_NAMEUNREACHABLE;
  345. status = __do_niscall3 (&dbp, prog, xargs, req, xres, resp, flags, cb);
  346. __nisbind_destroy (&dbp);
  347. return status;
  348. }
  349. static directory_obj *
  350. rec_dirsearch (const_nis_name name, directory_obj *dir, nis_error *status)
  351. {
  352. fd_result *fd_res;
  353. XDR xdrs;
  354. switch (nis_dir_cmp (name, dir->do_name))
  355. {
  356. case SAME_NAME:
  357. *status = NIS_SUCCESS;
  358. return dir;
  359. case NOT_SEQUENTIAL:
  360. /* NOT_SEQUENTIAL means, go one up and try it there ! */
  361. case HIGHER_NAME:
  362. { /* We need data from a parent domain */
  363. directory_obj *obj;
  364. const char *ndomain = __nis_domain_of (dir->do_name);
  365. /* The root server of our domain is a replica of the parent
  366. domain ! (Now I understand why a root server must be a
  367. replica of the parent domain) */
  368. fd_res = __nis_finddirectory (dir, ndomain);
  369. if (fd_res == NULL)
  370. {
  371. nis_free_directory (dir);
  372. *status = NIS_NOMEMORY;
  373. return NULL;
  374. }
  375. *status = fd_res->status;
  376. if (fd_res->status != NIS_SUCCESS)
  377. {
  378. /* Try the current directory obj, maybe it works */
  379. __free_fdresult (fd_res);
  380. return dir;
  381. }
  382. nis_free_directory (dir);
  383. obj = calloc (1, sizeof (directory_obj));
  384. if (obj == NULL)
  385. {
  386. __free_fdresult (fd_res);
  387. *status = NIS_NOMEMORY;
  388. return NULL;
  389. }
  390. xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
  391. fd_res->dir_data.dir_data_len, XDR_DECODE);
  392. _xdr_directory_obj (&xdrs, obj);
  393. xdr_destroy (&xdrs);
  394. __free_fdresult (fd_res);
  395. /* We have found a NIS+ server serving ndomain, now
  396. let us search for "name" */
  397. return rec_dirsearch (name, obj, status);
  398. }
  399. break;
  400. case LOWER_NAME:
  401. {
  402. directory_obj *obj;
  403. size_t namelen = strlen (name);
  404. char leaf[namelen + 3];
  405. char domain[namelen + 3];
  406. const char *ndomain;
  407. char *cp;
  408. strcpy (domain, name);
  409. do
  410. {
  411. if (domain[0] == '\0')
  412. {
  413. nis_free_directory (dir);
  414. return NULL;
  415. }
  416. nis_leaf_of_r (domain, leaf, sizeof (leaf));
  417. ndomain = __nis_domain_of (domain);
  418. memmove (domain, ndomain, strlen (ndomain) + 1);
  419. }
  420. while (nis_dir_cmp (domain, dir->do_name) != SAME_NAME);
  421. cp = strchr (leaf, '\0');
  422. *cp++ = '.';
  423. strcpy (cp, domain);
  424. fd_res = __nis_finddirectory (dir, leaf);
  425. if (fd_res == NULL)
  426. {
  427. nis_free_directory (dir);
  428. *status = NIS_NOMEMORY;
  429. return NULL;
  430. }
  431. *status = fd_res->status;
  432. if (fd_res->status != NIS_SUCCESS)
  433. {
  434. /* Try the current directory object, maybe it works */
  435. __free_fdresult (fd_res);
  436. return dir;
  437. }
  438. nis_free_directory (dir);
  439. obj = calloc (1, sizeof (directory_obj));
  440. if (obj == NULL)
  441. {
  442. __free_fdresult (fd_res);
  443. *status = NIS_NOMEMORY;
  444. return NULL;
  445. }
  446. xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
  447. fd_res->dir_data.dir_data_len, XDR_DECODE);
  448. _xdr_directory_obj (&xdrs, obj);
  449. xdr_destroy (&xdrs);
  450. __free_fdresult (fd_res);
  451. /* We have found a NIS+ server serving ndomain, now
  452. let us search for "name" */
  453. return rec_dirsearch (name, obj, status);
  454. }
  455. break;
  456. case BAD_NAME:
  457. nis_free_directory (dir);
  458. *status = NIS_BADNAME;
  459. return NULL;
  460. }
  461. nis_free_directory (dir);
  462. *status = NIS_FAIL;
  463. return NULL;
  464. }
  465. /* We try to query the current server for the searched object,
  466. maybe he know about it ? */
  467. static directory_obj *
  468. first_shoot (const_nis_name name, directory_obj *dir)
  469. {
  470. directory_obj *obj = NULL;
  471. fd_result *fd_res;
  472. XDR xdrs;
  473. if (nis_dir_cmp (name, dir->do_name) == SAME_NAME)
  474. return dir;
  475. fd_res = __nis_finddirectory (dir, name);
  476. if (fd_res == NULL)
  477. return NULL;
  478. if (fd_res->status == NIS_SUCCESS
  479. && (obj = calloc (1, sizeof (directory_obj))) != NULL)
  480. {
  481. xdrmem_create (&xdrs, fd_res->dir_data.dir_data_val,
  482. fd_res->dir_data.dir_data_len, XDR_DECODE);
  483. _xdr_directory_obj (&xdrs, obj);
  484. xdr_destroy (&xdrs);
  485. if (strcmp (dir->do_name, obj->do_name) != 0)
  486. {
  487. nis_free_directory (obj);
  488. obj = NULL;
  489. }
  490. }
  491. __free_fdresult (fd_res);
  492. if (obj != NULL)
  493. nis_free_directory (dir);
  494. return obj;
  495. }
  496. static struct nis_server_cache
  497. {
  498. int search_parent;
  499. int uses;
  500. unsigned int size;
  501. unsigned int server_used;
  502. unsigned int current_ep;
  503. time_t expires;
  504. char name[];
  505. } *nis_server_cache[16];
  506. static time_t nis_cold_start_mtime;
  507. __libc_lock_define_initialized (static, nis_server_cache_lock)
  508. static directory_obj *
  509. nis_server_cache_search (const_nis_name name, int search_parent,
  510. unsigned int *server_used, unsigned int *current_ep,
  511. struct timespec *now)
  512. {
  513. directory_obj *ret = NULL;
  514. int i;
  515. char *addr;
  516. XDR xdrs;
  517. struct stat64 st;
  518. int saved_errno = errno;
  519. if (stat64 ("/var/nis/NIS_COLD_START", &st) < 0)
  520. st.st_mtime = nis_cold_start_mtime + 1;
  521. __set_errno (saved_errno);
  522. __libc_lock_lock (nis_server_cache_lock);
  523. for (i = 0; i < 16; ++i)
  524. if (nis_server_cache[i] == NULL)
  525. continue;
  526. else if (st.st_mtime != nis_cold_start_mtime
  527. || now->tv_sec > nis_server_cache[i]->expires)
  528. {
  529. free (nis_server_cache[i]);
  530. nis_server_cache[i] = NULL;
  531. }
  532. else if (nis_server_cache[i]->search_parent == search_parent
  533. && strcmp (nis_server_cache[i]->name, name) == 0)
  534. {
  535. ret = calloc (1, sizeof (directory_obj));
  536. if (ret == NULL)
  537. break;
  538. addr = strchr (nis_server_cache[i]->name, '\0') + 8;
  539. addr = (char *) ((uintptr_t) addr & ~(uintptr_t) 7);
  540. xdrmem_create (&xdrs, addr, nis_server_cache[i]->size, XDR_DECODE);
  541. if (!_xdr_directory_obj (&xdrs, ret))
  542. {
  543. xdr_destroy (&xdrs);
  544. free (ret);
  545. ret = NULL;
  546. free (nis_server_cache[i]);
  547. nis_server_cache[i] = NULL;
  548. break;
  549. }
  550. xdr_destroy (&xdrs);
  551. *server_used = nis_server_cache[i]->server_used;
  552. *current_ep = nis_server_cache[i]->current_ep;
  553. break;
  554. }
  555. nis_cold_start_mtime = st.st_mtime;
  556. __libc_lock_unlock (nis_server_cache_lock);
  557. return ret;
  558. }
  559. static void
  560. nis_server_cache_add (const_nis_name name, int search_parent,
  561. directory_obj *dir, unsigned int server_used,
  562. unsigned int current_ep, struct timespec *now)
  563. {
  564. struct nis_server_cache **loc;
  565. struct nis_server_cache *new;
  566. struct nis_server_cache *old;
  567. int i;
  568. char *addr;
  569. unsigned int size;
  570. XDR xdrs;
  571. if (dir == NULL)
  572. return;
  573. size = xdr_sizeof ((xdrproc_t) _xdr_directory_obj, (char *) dir);
  574. new = calloc (1, sizeof (*new) + strlen (name) + 8 + size);
  575. if (new == NULL)
  576. return;
  577. new->search_parent = search_parent;
  578. new->uses = 1;
  579. new->expires = now->tv_sec + dir->do_ttl;
  580. new->size = size;
  581. new->server_used = server_used;
  582. new->current_ep = current_ep;
  583. addr = stpcpy (new->name, name) + 8;
  584. addr = (char *) ((uintptr_t) addr & ~(uintptr_t) 7);
  585. xdrmem_create(&xdrs, addr, size, XDR_ENCODE);
  586. if (!_xdr_directory_obj (&xdrs, dir))
  587. {
  588. xdr_destroy (&xdrs);
  589. free (new);
  590. return;
  591. }
  592. xdr_destroy (&xdrs);
  593. __libc_lock_lock (nis_server_cache_lock);
  594. /* Choose which entry should be evicted from the cache. */
  595. loc = &nis_server_cache[0];
  596. if (*loc != NULL)
  597. {
  598. for (i = 1; i < 16; ++i)
  599. if (nis_server_cache[i] == NULL)
  600. {
  601. loc = &nis_server_cache[i];
  602. break;
  603. }
  604. else if ((*loc)->uses > nis_server_cache[i]->uses
  605. || ((*loc)->uses == nis_server_cache[i]->uses
  606. && (*loc)->expires > nis_server_cache[i]->expires))
  607. loc = &nis_server_cache[i];
  608. }
  609. old = *loc;
  610. *loc = new;
  611. __libc_lock_unlock (nis_server_cache_lock);
  612. free (old);
  613. }
  614. nis_error
  615. __nisfind_server (const_nis_name name, int search_parent,
  616. directory_obj **dir, dir_binding *dbp, unsigned int flags)
  617. {
  618. nis_error result = NIS_SUCCESS;
  619. nis_error status;
  620. directory_obj *obj;
  621. struct timespec ts;
  622. unsigned int server_used = ~0;
  623. unsigned int current_ep = ~0;
  624. if (name == NULL)
  625. return NIS_BADNAME;
  626. if (*dir != NULL)
  627. return NIS_SUCCESS;
  628. clock_gettime (CLOCK_REALTIME, &ts);
  629. if ((flags & NO_CACHE) == 0)
  630. *dir = nis_server_cache_search (name, search_parent, &server_used,
  631. &current_ep, &ts);
  632. if (*dir != NULL)
  633. {
  634. unsigned int server_len = (*dir)->do_servers.do_servers_len;
  635. if (flags & MASTER_ONLY)
  636. {
  637. server_len = 1;
  638. if (server_used != 0)
  639. {
  640. server_used = ~0;
  641. current_ep = ~0;
  642. }
  643. }
  644. result = __nisbind_create (dbp, (*dir)->do_servers.do_servers_val,
  645. server_len, server_used, current_ep, flags);
  646. if (result != NIS_SUCCESS)
  647. {
  648. nis_free_directory (*dir);
  649. *dir = NULL;
  650. }
  651. return result;
  652. }
  653. int saved_errno = errno;
  654. *dir = readColdStartFile ();
  655. __set_errno (saved_errno);
  656. if (*dir == NULL)
  657. /* No /var/nis/NIS_COLD_START->no NIS+ installed. */
  658. return NIS_UNAVAIL;
  659. /* Try at first, if servers in "dir" know our object */
  660. const char *search_name = name;
  661. if (search_parent)
  662. search_name = __nis_domain_of (name);
  663. obj = first_shoot (search_name, *dir);
  664. if (obj == NULL)
  665. {
  666. obj = rec_dirsearch (search_name, *dir, &status);
  667. if (obj == NULL)
  668. result = status;
  669. }
  670. if (result == NIS_SUCCESS)
  671. {
  672. unsigned int server_len = obj->do_servers.do_servers_len;
  673. if (flags & MASTER_ONLY)
  674. server_len = 1;
  675. result = __nisbind_create (dbp, obj->do_servers.do_servers_val,
  676. server_len, ~0, ~0, flags);
  677. if (result == NIS_SUCCESS)
  678. {
  679. if ((flags & MASTER_ONLY) == 0
  680. || obj->do_servers.do_servers_len == 1)
  681. {
  682. server_used = dbp->server_used;
  683. current_ep = dbp->current_ep;
  684. }
  685. if ((flags & NO_CACHE) == 0)
  686. nis_server_cache_add (name, search_parent, obj,
  687. server_used, current_ep, &ts);
  688. }
  689. else
  690. {
  691. nis_free_directory (obj);
  692. obj = NULL;
  693. }
  694. }
  695. *dir = obj;
  696. return result;
  697. }
  698. nis_error
  699. __prepare_niscall (const_nis_name name, directory_obj **dirp,
  700. dir_binding *bptrp, unsigned int flags)
  701. {
  702. nis_error retcode = __nisfind_server (name, 1, dirp, bptrp, flags);
  703. if (__glibc_unlikely (retcode != NIS_SUCCESS))
  704. return retcode;
  705. do
  706. if (__nisbind_connect (bptrp) == NIS_SUCCESS)
  707. return NIS_SUCCESS;
  708. while (__nisbind_next (bptrp) == NIS_SUCCESS);
  709. __nisbind_destroy (bptrp);
  710. memset (bptrp, '\0', sizeof (*bptrp));
  711. retcode = NIS_NAMEUNREACHABLE;
  712. nis_free_directory (*dirp);
  713. *dirp = NULL;
  714. return retcode;
  715. }
  716. libnsl_hidden_nolink_def (__prepare_niscall, GLIBC_PRIVATE)
  717. nis_error
  718. __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
  719. caddr_t req, xdrproc_t xres, caddr_t resp, unsigned int flags,
  720. nis_cb *cb)
  721. {
  722. dir_binding bptr;
  723. directory_obj *dir = NULL;
  724. int saved_errno = errno;
  725. nis_error retcode = __prepare_niscall (name, &dir, &bptr, flags);
  726. if (retcode == NIS_SUCCESS)
  727. {
  728. retcode = __do_niscall3 (&bptr, prog, xargs, req, xres, resp, flags, cb);
  729. __nisbind_destroy (&bptr);
  730. nis_free_directory (dir);
  731. }
  732. __set_errno (saved_errno);
  733. return retcode;
  734. }