(root)/
glibc-2.38/
nis/
nis_findserv.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 <string.h>
      19  #include <time.h>
      20  #include <unistd.h>
      21  #include <sys/ioctl.h>
      22  #include <sys/socket.h>
      23  #include <rpc/pmap_prot.h>
      24  #include <rpc/pmap_clnt.h>
      25  #include <rpcsvc/nis.h>
      26  
      27  #include "nis_intern.h"
      28  
      29  /* Private data kept per client handle, from sunrpc/clnt_udp.c */
      30  struct cu_data
      31    {
      32      int cu_sock;
      33      bool_t cu_closeit;
      34      struct sockaddr_in cu_raddr;
      35      int cu_rlen;
      36      struct timeval cu_wait;
      37      struct timeval cu_total;
      38      struct rpc_err cu_error;
      39      XDR cu_outxdrs;
      40      u_int cu_xdrpos;
      41      u_int cu_sendsz;
      42      char *cu_outbuf;
      43      u_int cu_recvsz;
      44      char cu_inbuf[1];
      45    };
      46  
      47  
      48  /*
      49   * Find the mapped port for program,version.
      50   * Calls the pmap service remotely to do the lookup.
      51   * Returns 0 if no map exists.
      52   */
      53  u_short
      54  __pmap_getnisport (struct sockaddr_in *address, u_long program,
      55  		   u_long version, u_int protocol)
      56  {
      57    return __libc_rpc_getport (address, program, version, protocol, 1, 1);
      58  }
      59  
      60  /* This is now the public function, which should find the fastest server */
      61  
      62  struct findserv_req
      63  {
      64    struct sockaddr_in sin;
      65    uint32_t xid;
      66    u_int server_nr;
      67    u_int server_ep;
      68  };
      69  
      70  
      71  static long int
      72  __nis_findfastest_with_timeout (dir_binding *bind,
      73  				const struct timeval *timeout)
      74  {
      75    static const struct timeval TIMEOUT00 = { 0, 0 };
      76    struct findserv_req *pings;
      77    struct sockaddr_in sin, saved_sin;
      78    int found = -1;
      79    uint32_t xid_seed;
      80    int sock, dontblock = 1;
      81    CLIENT *clnt;
      82    u_long i, j, pings_count, pings_max, fastest = -1;
      83    struct cu_data *cu;
      84  
      85    pings_max = bind->server_len * 2;	/* Reserve a little bit more memory
      86  					   for multihomed hosts */
      87    pings_count = 0;
      88    pings = malloc (sizeof (struct findserv_req) * pings_max);
      89    xid_seed = (uint32_t) (time (NULL) ^ getpid ());
      90  
      91    if (__glibc_unlikely (pings == NULL))
      92      return -1;
      93  
      94    memset (&sin, '\0', sizeof (sin));
      95    sin.sin_family = AF_INET;
      96    for (i = 0; i < bind->server_len; i++)
      97      for (j = 0; j < bind->server_val[i].ep.ep_len; ++j)
      98        if (strcmp (bind->server_val[i].ep.ep_val[j].family, "inet") == 0)
      99  	if ((bind->server_val[i].ep.ep_val[j].proto == NULL)
     100  	    || (bind->server_val[i].ep.ep_val[j].proto[0] == '-')
     101  	    || (bind->server_val[i].ep.ep_val[j].proto[0] == '\0'))
     102  	  {
     103  	    sin.sin_addr.s_addr =
     104  	      inetstr2int (bind->server_val[i].ep.ep_val[j].uaddr);
     105  	    if (sin.sin_addr.s_addr == 0)
     106  	      continue;
     107  	    sin.sin_port = htons (__pmap_getnisport (&sin, NIS_PROG,
     108  						     NIS_VERSION,
     109  						     IPPROTO_UDP));
     110  	    if (sin.sin_port == 0)
     111  	      continue;
     112  
     113  	    if (pings_count >= pings_max)
     114  	      {
     115  		struct findserv_req *new_pings;
     116  
     117  		pings_max += 10;
     118  		new_pings = realloc (pings, sizeof (struct findserv_req) *
     119  				     pings_max);
     120  		if (__glibc_unlikely (new_pings == NULL))
     121  		  {
     122  		    free (pings);
     123  		    return -1;
     124  		  }
     125  		pings = new_pings;
     126  	      }
     127  	    memcpy ((char *) &pings[pings_count].sin, (char *) &sin,
     128  		    sizeof (sin));
     129  	    memcpy ((char *)&saved_sin, (char *)&sin, sizeof (sin));
     130  	    pings[pings_count].xid = xid_seed + pings_count;
     131  	    pings[pings_count].server_nr = i;
     132  	    pings[pings_count].server_ep = j;
     133  	    ++pings_count;
     134  	  }
     135  
     136    /* Make sure at least one server was assigned */
     137    if (pings_count == 0)
     138      {
     139        free (pings);
     140        return -1;
     141      }
     142  
     143    /* Create RPC handle */
     144    sock = socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
     145    clnt = clntudp_create (&saved_sin, NIS_PROG, NIS_VERSION, *timeout, &sock);
     146    if (clnt == NULL)
     147      {
     148        close (sock);
     149        free (pings);
     150        return -1;
     151      }
     152    auth_destroy (clnt->cl_auth);
     153    clnt->cl_auth = authunix_create_default ();
     154    cu = (struct cu_data *) clnt->cl_private;
     155    ioctl (sock, FIONBIO, &dontblock);
     156    /* Send to all servers the NULLPROC */
     157    for (i = 0; i < pings_count; ++i)
     158      {
     159        /* clntudp_call() will increment, subtract one */
     160        *((uint32_t *) (cu->cu_outbuf)) = pings[i].xid - 1;
     161        memcpy ((char *) &cu->cu_raddr, (char *) &pings[i].sin,
     162  	      sizeof (struct sockaddr_in));
     163        /* Transmit to NULLPROC, return immediately. */
     164        clnt_call (clnt, NULLPROC,
     165  		 (xdrproc_t) xdr_void, (caddr_t) 0,
     166  		 (xdrproc_t) xdr_void, (caddr_t) 0, TIMEOUT00);
     167      }
     168  
     169    while (found == -1) {
     170      /* Receive reply from NULLPROC asynchronously. Note null inproc. */
     171      int rc = clnt_call (clnt, NULLPROC,
     172  			(xdrproc_t) NULL, (caddr_t) 0,
     173  			(xdrproc_t) xdr_void, (caddr_t) 0,
     174  			*timeout);
     175      if (RPC_SUCCESS == rc) {
     176        uint32_t val;
     177        memcpy (&val, cu->cu_inbuf, sizeof (uint32_t));
     178        fastest = val - xid_seed;
     179        if (fastest < pings_count) {
     180  	bind->server_used = pings[fastest].server_nr;
     181  	bind->current_ep = pings[fastest].server_ep;
     182  	found = 1;
     183        }
     184      } else {
     185        /*      clnt_perror(clnt, "__nis_findfastest"); */
     186        break;
     187      }
     188    }
     189  
     190  
     191    auth_destroy (clnt->cl_auth);
     192    clnt_destroy (clnt);
     193    close (sock);
     194  
     195    free (pings);
     196  
     197    return found;
     198  }
     199  
     200  
     201  long int
     202  __nis_findfastest (dir_binding *bind)
     203  {
     204    struct timeval timeout = { __NIS_PING_TIMEOUT_START, 0 };
     205    long int found = -1;
     206    long int retry = __NIS_PING_RETRY + 1;
     207  
     208    while (retry--)
     209      {
     210        found = __nis_findfastest_with_timeout (bind, &timeout);
     211        if (found != -1)
     212  	break;
     213        timeout.tv_sec += __NIS_PING_TIMEOUT_INCREMENT;
     214      }
     215  
     216    return found;
     217  }