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