about summary refs log tree commit diff
path: root/nis
diff options
context:
space:
mode:
Diffstat (limited to 'nis')
-rw-r--r--nis/Makefile3
-rw-r--r--nis/TODO1
-rw-r--r--nis/lckcache.c8
-rw-r--r--nis/nis_call.c251
-rw-r--r--nis/nis_findserv.c251
-rw-r--r--nis/nis_intern.h24
-rw-r--r--nis/nis_lookup.c4
-rw-r--r--nis/nis_ping.c8
-rw-r--r--nis/nis_table.c6
-rw-r--r--nis/nis_util.c16
-rw-r--r--nis/nss_compat/compat-grp.c29
-rw-r--r--nis/nss_compat/compat-pwd.c43
-rw-r--r--nis/nss_compat/compat-spwd.c29
-rw-r--r--nis/ypclnt.c16
14 files changed, 535 insertions, 154 deletions
diff --git a/nis/Makefile b/nis/Makefile
index c10c175e7d..e7dd50c1d9 100644
--- a/nis/Makefile
+++ b/nis/Makefile
@@ -50,7 +50,8 @@ libnsl-routines = yp_xdr ypclnt ypupdate_xdr \
 		  nis_verifygroup nis_ismember nis_addmember nis_util\
 		  nis_removemember nis_creategroup nis_destroygroup\
 		  nis_print_group_entry nis_domain_of nis_domain_of_r\
-		  nis_modify nis_remove nis_add nis_defaults lckcache
+		  nis_modify nis_remove nis_add nis_defaults lckcache\
+		  nis_findserv
 libnsl-map	= libnsl.map
 
 libnss_compat-routines	:= $(addprefix compat-,grp pwd spwd) nisplus-parser
diff --git a/nis/TODO b/nis/TODO
index 0fe695d78b..f34bc09a06 100644
--- a/nis/TODO
+++ b/nis/TODO
@@ -9,4 +9,3 @@
  * Missing flags:
 	- FOLLOW_PATH	(nis_list, not supported)
 	- ALL_RESULTS	(nis_list, not supported, needs server callback)
-	- NO_CACHE	(__do_niscall, cache not supported yet)
diff --git a/nis/lckcache.c b/nis/lckcache.c
index ead577372e..f8c0a97296 100644
--- a/nis/lckcache.c
+++ b/nis/lckcache.c
@@ -1,6 +1,6 @@
 /* Handle locking of NIS+ cache file.
-   Copyright (C) 1996 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
+   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library and based on shadow/lckfile.c.
 
    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public License as
@@ -30,7 +30,7 @@
 
 /* How long to wait for getting the lock before returning with an
    error.  */
-#define TIMEOUT 15 /* sec */
+#define TIMEOUT 5 /* sec */
 
 
 /* File descriptor for lock file.  */
@@ -141,7 +141,7 @@ __nis_lock_cache (void)
   memset (&fl, '\0', sizeof (struct flock));
   fl.l_type = F_RDLCK;
   fl.l_whence = SEEK_SET;
-  result = fcntl (lock_fd, F_SETLK, &fl);
+  result = fcntl (lock_fd, F_SETLKW, &fl);
 
   RETURN_CLEAR_ALARM (result);
 }
diff --git a/nis/nis_call.c b/nis/nis_call.c
index f25b8017a5..1dfb12944a 100644
--- a/nis/nis_call.c
+++ b/nis/nis_call.c
@@ -29,30 +29,7 @@
 
 static struct timeval TIMEOUT = {10, 0};
 
-struct dir_binding
-{
-  CLIENT *clnt;                  /* RPC CLIENT handle */
-  nis_server *server_val;        /* List of servers */
-  u_int server_len;              /* # of servers */
-  u_int server_used;             /* Which server we are bind in the moment ? */
-  u_int trys;                    /* How many server have we tried ? */
-  bool_t master_only;            /* Is only binded to the master */
-  bool_t use_auth;               /* Do we use AUTH ? */
-  bool_t use_udp;                /* Do we use UDP ? */
-  time_t create;                 /* Binding creation time */
-  struct sockaddr_in addr;       /* Server's IP address */
-  int socket;                    /* Server's local socket */
-  unsigned short port;           /* Local port */
-};
-typedef struct dir_binding dir_binding;
-
-static inline u_int
-__nis_ping (const nis_server *serv, u_int serv_len)
-{
-  return 0;
-}
-
-static unsigned long
+unsigned long
 inetstr2int (const char *str)
 {
   char buffer[strlen (str) + 3];
@@ -92,12 +69,7 @@ __bind_destroy (dir_binding *bind)
 static nis_error
 __bind_next (dir_binding *bind)
 {
-  if (bind->trys >= bind->server_len)
-    return NIS_FAIL;
-  
-  bind->server_used++;
-  if (bind->server_used >= bind->server_len)
-    bind->server_used = 0;
+  u_int j;
 
   if (bind->clnt != NULL)
     {
@@ -106,8 +78,38 @@ __bind_next (dir_binding *bind)
       clnt_destroy (bind->clnt);
       bind->clnt = NULL;
     }
-  
-  return NIS_SUCCESS;
+
+  if (bind->trys >= bind->server_len)
+    return NIS_FAIL;
+
+  for (j = bind->current_ep + 1;
+       j < bind->server_val[bind->server_used].ep.ep_len; ++j)
+    if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
+		"inet") == 0)
+      if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
+		  "-") == 0)
+	{
+	  bind->current_ep = j;
+	  return NIS_SUCCESS;
+	}
+
+  ++bind->trys;
+  ++bind->server_used;
+  if (bind->server_used >= bind->server_len)
+    bind->server_used = 0;
+
+  for (j = bind->current_ep + 1;
+       j < bind->server_val[bind->server_used].ep.ep_len; ++j)
+    if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].family,
+		"inet") == 0)
+      if (strcmp (bind->server_val[bind->server_used].ep.ep_val[j].proto,
+		  "-") == 0)
+	{
+	  bind->current_ep = j;
+	  return NIS_SUCCESS;
+	}
+
+  return NIS_FAIL;
 }
 
 static nis_error
@@ -116,7 +118,6 @@ __bind_connect (dir_binding *dbp)
   struct sockaddr_in check;
   nis_server *serv;
   int checklen;
-  u_int i;
 
   if (dbp == NULL)
     return NIS_FAIL;
@@ -125,26 +126,10 @@ __bind_connect (dir_binding *dbp)
 
   memset (&dbp->addr, '\0', sizeof (dbp->addr));
   dbp->addr.sin_family = AF_INET;
-  for (i = 0; i < serv->ep.ep_len; ++i)
-    {
-      if (strcmp (serv->ep.ep_val[i].family, "inet") == 0)
-	{
-	  if (dbp->use_udp)
-	    {
-	      if (strcmp (serv->ep.ep_val[i].proto, "udp") == 0)
-		dbp->addr.sin_addr.s_addr =
-		  inetstr2int (serv->ep.ep_val[i].uaddr);
-	      else
-		continue;
-	    }
-	  else
-	    if (strcmp (serv->ep.ep_val[i].proto, "tcp") == 0)
-	      dbp->addr.sin_addr.s_addr =
-		inetstr2int (serv->ep.ep_val[i].uaddr);
-	}
-      else
-	continue;
-    }
+
+  dbp->addr.sin_addr.s_addr =
+    inetstr2int (serv->ep.ep_val[dbp->current_ep].uaddr);
+
   if (dbp->addr.sin_addr.s_addr == 0)
     return NIS_FAIL;
 
@@ -155,15 +140,15 @@ __bind_connect (dir_binding *dbp)
   else
     dbp->clnt = clnttcp_create (&dbp->addr, NIS_PROG, NIS_VERSION,
 				 &dbp->socket, 0, 0);
-  
+
   if (dbp->clnt == NULL)
     return NIS_RPCERROR;
-  
+
   clnt_control (dbp->clnt, CLSET_TIMEOUT, (caddr_t)&TIMEOUT);
   /* If the program exists, close the socket */
   if (fcntl (dbp->socket, F_SETFD, 1) == -1)
     perror (_("fcntl: F_SETFD"));
-  
+
   if (dbp->use_auth)
     {
 #if defined(HAVE_SECURE_RPC)
@@ -171,7 +156,7 @@ __bind_connect (dir_binding *dbp)
 	{
 	  char netname[MAXNETNAMELEN+1];
 	  char *p;
-	  
+
 	  p = stpcpy (netname, "unix.");
 	  strncpy (p, serv->name,MAXNETNAMELEN-5);
 	  netname[MAXNETNAMELEN] = '\0';
@@ -187,7 +172,7 @@ __bind_connect (dir_binding *dbp)
 	dbp->clnt->cl_auth = authunix_create_default ();
       dbp->use_auth = TRUE;
     }
-  
+
   /* Get port for sanity checks later */
   checklen = sizeof (struct sockaddr_in);
   memset (&check, 0, checklen);
@@ -207,11 +192,11 @@ __bind_create (const nis_server *serv_val, u_int serv_len, u_long flags)
 {
   dir_binding *dbp;
   u_int i;
-  
+
   dbp = calloc (1, sizeof (dir_binding));
   if (dbp == NULL)
     return NULL;
-  
+
   dbp->server_len = serv_len;
   dbp->server_val = calloc (1, sizeof (nis_server) * serv_len);
   if (dbp->server_val == NULL)
@@ -219,17 +204,34 @@ __bind_create (const nis_server *serv_val, u_int serv_len, u_long flags)
       free (dbp);
       return NULL;
     }
-  
+
+  if (flags & USE_DGRAM)
+    dbp->use_udp = TRUE;
+  else
+    dbp->use_udp = FALSE;
+
+  if (flags & NO_AUTHINFO)
+    dbp->use_auth = FALSE;
+  else
+    dbp->use_auth = TRUE;
+
+  if (flags & MASTER_ONLY)
+    dbp->master_only = TRUE;
+  else
+    dbp->master_only = FALSE;
+
+  dbp->trys = 1;
+
   for (i = 0; i < serv_len; ++i)
     {
       if (serv_val[i].name != NULL)
 	dbp->server_val[i].name = strdup (serv_val[i].name);
-      
+
       dbp->server_val[i].ep.ep_len = serv_val[i].ep.ep_len;
       if (dbp->server_val[i].ep.ep_len > 0)
 	{
 	  unsigned long j;
-	  
+
 	  dbp->server_val[i].ep.ep_val =
 	    malloc (serv_val[i].ep.ep_len * sizeof (endpoint));
 	  for (j = 0; j < dbp->server_val[i].ep.ep_len; ++j)
@@ -267,24 +269,12 @@ __bind_create (const nis_server *serv_val, u_int serv_len, u_long flags)
       else
 	dbp->server_val[i].pkey.n_bytes = NULL;
     }
-  
-  dbp->server_used = __nis_ping (dbp->server_val, dbp->server_len);
-  if (flags & USE_DGRAM)
-    dbp->use_udp = TRUE;
-  else
-    dbp->use_udp = FALSE;
-
-  if (flags & NO_AUTHINFO)
-    dbp->use_auth = FALSE;
-  else
-    dbp->use_auth = TRUE;
 
-  if (flags & MASTER_ONLY)
-    dbp->master_only = TRUE;
-  else
-    dbp->master_only = FALSE;
-
-  dbp->trys = 1;
+  if (__nis_findfastest (dbp) < 1)
+    {
+      __bind_destroy (dbp);
+      return NULL;
+    }
 
   return dbp;
 }
@@ -298,10 +288,11 @@ __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
   nis_error retcode;
   dir_binding *dbp;
 
-  if (flags & MASTER_ONLY) 
+  if (flags & MASTER_ONLY)
     server_len = 1;
-  
-  dbp = __bind_create (server, server_len, flags);
+
+  if ((dbp = __bind_create (server, server_len, flags)) == NULL)
+    return NIS_UNAVAIL;
   while (__bind_connect (dbp) != NIS_SUCCESS)
     {
       if (__bind_next (dbp) != NIS_SUCCESS)
@@ -315,7 +306,7 @@ __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
     {
     again:
       result = clnt_call (dbp->clnt, prog, xargs, req, xres, resp, TIMEOUT);
-      
+
       if (result != RPC_SUCCESS)
 	{
 	  clnt_perror (dbp->clnt, "__do_niscall2: clnt_call");
@@ -336,25 +327,56 @@ __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
 	    case NIS_IBREMOVE:
 	    case NIS_IBFIRST:
 	    case NIS_IBNEXT:
-	      if ((((nis_result *)xres)->status != NIS_SUCCESS) &&
-		  (((nis_result *)xres)->status != NIS_S_SUCCESS))
-		if (__bind_next (dbp) == NIS_SUCCESS)
+	      if ((((nis_result *)xres)->status == NIS_NOTFOUND) ||
+		  (((nis_result *)xres)->status == NIS_NOSUCHNAME) ||
+		  (((nis_result *)xres)->status == NIS_NOT_ME))
+		{
+		  if (__bind_next (dbp) == NIS_SUCCESS)
+		    while (__bind_connect (dbp) != NIS_SUCCESS)
+		      {
+			if (__bind_next (dbp) != NIS_SUCCESS)
+			  {
+			    __bind_destroy (dbp);
+			    return NIS_SUCCESS;
+			  }
+		      }
 		  goto again;
+		}
 	    case NIS_FINDDIRECTORY:
-	      if (((fd_result *)xres)->status != NIS_SUCCESS)
-		if (__bind_next (dbp) == NIS_SUCCESS)
+	      if ((((fd_result *)xres)->status == NIS_NOTFOUND) ||
+		  (((fd_result *)xres)->status == NIS_NOSUCHNAME) ||
+		  (((fd_result *)xres)->status == NIS_NOT_ME))
+		{
+		  if (__bind_next (dbp) == NIS_SUCCESS)
+		    while (__bind_connect (dbp) != NIS_SUCCESS)
+		      {
+			if (__bind_next (dbp) != NIS_SUCCESS)
+			  {
+			    __bind_destroy (dbp);
+			    return NIS_SUCCESS;
+			  }
+		      }
 		  goto again;
-	      break;
-#if 0
-	    case NIS_STATUS: /* nis_taglist */
-	    case NIS_SERVSTATE:
+		}
 	      break;
 	    case NIS_DUMPLOG: /* log_result */
 	    case NIS_DUMP:
+	      if ((((log_result *)xres)->lr_status == NIS_NOTFOUND) ||
+		  (((log_result *)xres)->lr_status == NIS_NOSUCHNAME) ||
+		  (((log_result *)xres)->lr_status == NIS_NOT_ME))
+		{
+		  if (__bind_next (dbp) == NIS_SUCCESS)
+		    while (__bind_connect (dbp) != NIS_SUCCESS)
+		      {
+			if (__bind_next (dbp) != NIS_SUCCESS)
+			  {
+			    __bind_destroy (dbp);
+			    return NIS_SUCCESS;
+			  }
+		      }
+		  goto again;
+		}
 	      break;
-	    case NIS_CHECKPOINT: /* cp_result */
-	      break;
-#endif
 	    default:
 	      break;
 	    }
@@ -363,12 +385,13 @@ __do_niscall2 (const nis_server *server, u_int server_len, u_long prog,
 	}
     }
   while ((flags & HARD_LOOKUP) && retcode == NIS_RPCERROR);
-  
-  return retcode; 
+
+  return retcode;
 }
 
 static directory_obj *
-rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags)
+rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags,
+	       nis_error *status)
 {
   fd_result *fd_res;
   XDR xdrs;
@@ -399,6 +422,7 @@ rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags)
   switch (nis_dir_cmp (domain, dir->do_name))
     {
     case SAME_NAME:
+      *status = NIS_SUCCESS;
       return dir;
     case NOT_SEQUENTIAL:
       /* NOT_SEQUENTIAL means, go one up and try it there ! */
@@ -413,6 +437,7 @@ rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags)
 	   domain ! (Now I understand why a root server must be a
 	   replica of the parent domain) */
 	fd_res = __nis_finddirectory (dir, ndomain);
+	*status = fd_res->status;
 	if (fd_res->status != NIS_SUCCESS)
 	  {
 	    nis_free_directory (dir);
@@ -431,7 +456,7 @@ rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags)
 	    /* We have found a NIS+ server serving ndomain, now
 	       let us search for "name" */
 	    nis_free_directory (dir);
-	    return rec_dirsearch (name, obj, flags);
+	    return rec_dirsearch (name, obj, flags, status);
 	  }
 	else
 	  {
@@ -447,7 +472,7 @@ rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags)
 	char leaf [strlen (name) + 3];
 	char ndomain [strlen (name) + 3];
 	char *cp;
-	
+
 	do
 	  {
 	    if (strlen (domain) == 0)
@@ -463,8 +488,9 @@ rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags)
 	cp = strchr (leaf, '\0');
 	*cp++ = '.';
 	strcpy (cp, domain);
-	
+
 	fd_res = __nis_finddirectory (dir, leaf);
+	*status = fd_res->status;
 	if (fd_res->status != NIS_SUCCESS)
 	  {
 	    nis_free_directory (dir);
@@ -483,15 +509,17 @@ rec_dirsearch (const_nis_name name, directory_obj *dir, u_long flags)
 	    /* We have found a NIS+ server serving ndomain, now
 	       let us search for "name" */
 	    nis_free_directory (dir);
-	    return rec_dirsearch (name, obj, flags);
+	    return rec_dirsearch (name, obj, flags, status);
 	  }
       }
     break;
     case BAD_NAME:
       nis_free_directory (dir);
+      *status = NIS_BADNAME;
       return NULL;
     }
   nis_free_directory (dir);
+  *status = NIS_FAIL;
   return NULL;
 }
 
@@ -509,19 +537,20 @@ __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
 
   if ((flags & NO_CACHE) !=  NO_CACHE)
     dir = __cache_search (name);
-  
+
   if (dir == NULL)
     {
+      nis_error status;
       dir = readColdStartFile ();
       if (dir == NULL) /* No /var/nis/NIS_COLD_START->no NIS+ installed */
 	return NIS_UNAVAIL;
-      
-      dir = rec_dirsearch (name, dir, flags);
+
+      dir = rec_dirsearch (name, dir, flags, &status);
       if (dir == NULL)
-	return NIS_NOTFOUND;
+	return status;
     }
 
-  if (flags & MASTER_ONLY) 
+  if (flags & MASTER_ONLY)
     {
       server = dir->do_servers.do_servers_val;
       server_len = 1;
@@ -531,11 +560,11 @@ __do_niscall (const_nis_name name, u_long prog, xdrproc_t xargs,
       server = dir->do_servers.do_servers_val;
       server_len = dir->do_servers.do_servers_len;
     }
-  
-  
+
+
   retcode = __do_niscall2 (server, server_len, prog, xargs, req, xres, resp,
 			   flags);
-  
+
   nis_free_directory (dir);
 
   return retcode;
diff --git a/nis/nis_findserv.c b/nis/nis_findserv.c
new file mode 100644
index 0000000000..a04abd8056
--- /dev/null
+++ b/nis/nis_findserv.c
@@ -0,0 +1,251 @@
+/* Copyright (C) 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1997.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <rpcsvc/nis.h>
+
+#include "nis_intern.h"
+
+/* Private data kept per client handle, from sunrpc/clnt_udp.c */
+struct cu_data
+  {
+    int cu_sock;
+    bool_t cu_closeit;
+    struct sockaddr_in cu_raddr;
+    int cu_rlen;
+    struct timeval cu_wait;
+    struct timeval cu_total;
+    struct rpc_err cu_error;
+    XDR cu_outxdrs;
+    u_int cu_xdrpos;
+    u_int cu_sendsz;
+    char *cu_outbuf;
+    u_int cu_recvsz;
+    char cu_inbuf[1];
+  };
+
+
+/* The following is the original routine from sunrpc/pm_getport.c.
+   The only change is the much shorter timeout. */
+/*
+ * pmap_getport.c
+ * Client interface to pmap rpc service.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+/*
+ * Find the mapped port for program,version.
+ * Calls the pmap service remotely to do the lookup.
+ * Returns 0 if no map exists.
+ */
+static u_short
+__pmap_getport (struct sockaddr_in *address, u_long program,
+		u_long version, u_int protocol)
+{
+  const struct timeval timeout = {1, 0};
+  const struct timeval tottimeout = {1, 0};
+  u_short port = 0;
+  int socket = -1;
+  CLIENT *client;
+  struct pmap parms;
+
+  address->sin_port = htons (PMAPPORT);
+  client = clntudp_bufcreate (address, PMAPPROG, PMAPVERS, timeout, &socket,
+			      RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+  if (client != (CLIENT *) NULL)
+    {
+      parms.pm_prog = program;
+      parms.pm_vers = version;
+      parms.pm_prot = protocol;
+      parms.pm_port = 0;	/* not needed or used */
+      if (CLNT_CALL (client, PMAPPROC_GETPORT, (xdrproc_t) xdr_pmap,
+		     (caddr_t) & parms, (xdrproc_t) xdr_u_short,
+		     (caddr_t) & port, tottimeout) != RPC_SUCCESS)
+	{
+	  rpc_createerr.cf_stat = RPC_PMAPFAILURE;
+	  clnt_geterr (client, &rpc_createerr.cf_error);
+	}
+      else if (port == 0)
+	{
+	  rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+	}
+      CLNT_DESTROY (client);
+    }
+  /* (void)close(socket); CLNT_DESTROY already closed it */
+  address->sin_port = 0;
+  return port;
+}
+
+/* Transmit to NULLPROC, return immediately. */
+static void *
+nis_null_3_send (void *argp, CLIENT * clnt)
+{
+  static char clnt_res;
+  struct timeval TIMEOUT = {0, 0};
+
+  memset ((char *) &clnt_res, 0, sizeof (clnt_res));
+  if (clnt_call (clnt, NULLPROC,
+		 (xdrproc_t) xdr_void, (caddr_t) argp,
+		 (xdrproc_t) xdr_void, (caddr_t) & clnt_res,
+		 TIMEOUT) != RPC_SUCCESS)
+    return NULL;
+  return (void *) &clnt_res;
+}
+
+/* Receive request from NULLPROC asynchronously. */
+static void *
+nis_null_3_recv (void *argp, CLIENT * clnt)
+{
+  static char clnt_res;
+  struct timeval TIMEOUT = {0, 0};
+
+  memset ((char *) &clnt_res, 0, sizeof (clnt_res));
+  if (clnt_call (clnt, NULLPROC,
+		 (xdrproc_t) NULL, (caddr_t) argp,
+		 (xdrproc_t) xdr_void, (caddr_t) & clnt_res,
+		 TIMEOUT) != RPC_SUCCESS)
+    return NULL;
+  return (void *) &clnt_res;
+}
+
+/* This is now the public functions, which should find the fastest server */
+
+struct findserv_req
+{
+  struct sockaddr_in sin;
+  u_long xid;
+  u_int server_nr;
+  u_int server_ep;
+};
+
+long
+__nis_findfastest (dir_binding * bind)
+{
+  struct timeval TIMEOUT = {5, 0};
+  struct findserv_req **pings;
+  struct sockaddr_in sin;
+  int found = -1;
+  time_t xid_seed, xid_lookup;
+  int sock, dontblock = 1;
+  CLIENT *clnt;
+  void *foo = NULL;
+  u_long i, j, pings_count, pings_max;
+  struct cu_data *cu;
+
+  pings_max = bind->server_len * 2;	/* Reserve a little bit more memory
+					   for multihomed hosts */
+  pings_count = 0;
+  pings = malloc (sizeof (struct findserv_req *) * pings_max);
+  xid_seed = time (NULL) ^ getpid ();
+
+  memset (&sin, '\0', sizeof (sin));
+  sin.sin_family = AF_INET;
+  for (i = 0; i < bind->server_len; i++)
+    for (j = 0; j < bind->server_val[i].ep.ep_len; ++j)
+      if (strcmp (bind->server_val[i].ep.ep_val[j].family, "inet") == 0)
+	if (strcmp (bind->server_val[i].ep.ep_val[j].proto, "-") == 0)
+	  {
+	    sin.sin_addr.s_addr =
+	      inetstr2int (bind->server_val[i].ep.ep_val[j].uaddr);
+	    if (sin.sin_addr.s_addr == 0)
+	      continue;
+	    sin.sin_port = htons (__pmap_getport (&sin, NIS_PROG,
+						  NIS_VERSION, IPPROTO_UDP));
+	    if (sin.sin_port == 0)
+	      continue;
+
+	    if (pings_count >= pings_max)
+	      {
+		pings_max += 10;
+		pings = realloc (pings, sizeof (struct findserv_req) *
+				 pings_max);
+	      }
+	    pings[pings_count] = calloc (1, sizeof (struct findserv_req));
+	    memcpy ((char *) &pings[pings_count]->sin, (char *) &sin,
+		    sizeof (sin));
+	    pings[pings_count]->xid = xid_seed;
+	    pings[pings_count]->server_nr = i;
+	    pings[pings_count]->server_ep = j;
+	    ++xid_seed;
+	    ++pings_count;
+	  }
+
+  /* Make sure at least one server was assigned */
+  if (pings_count == 0)
+    {
+      free (pings);
+      return -1;
+    }
+
+  /* Create RPC handle */
+  sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  clnt = clntudp_create (&sin, NIS_PROG, NIS_VERSION, TIMEOUT, &sock);
+  if (clnt == NULL)
+    {
+      close (sock);
+      for (i = 0; i < pings_count; ++i)
+	free (pings[i]);
+      free (pings);
+      return -1;
+    }
+  clnt->cl_auth = authunix_create_default ();
+  cu = (struct cu_data *) clnt->cl_private;
+  TIMEOUT.tv_sec = 0;
+  clnt_control (clnt, CLSET_TIMEOUT, (char *) &TIMEOUT);
+  ioctl (sock, FIONBIO, &dontblock);
+
+  /* Send to all servers the NULLPROC */
+  for (i = 0; i < pings_count; ++i)
+    {
+      /* clntudp_call() will increment, subtract one */
+      *((u_int32_t *) (cu->cu_outbuf)) = pings[i]->xid - 1;
+      bcopy ((char *) &pings[i]->sin, (char *) &cu->cu_raddr,
+	     sizeof (struct sockaddr_in));
+      nis_null_3_send (foo, clnt);
+    }
+
+  /* Receive reply */
+  nis_null_3_recv (foo, clnt);
+
+  xid_lookup = *((u_int32_t *) (cu->cu_inbuf));
+  for (i = 0; i < pings_count; i++)
+    {
+      if (pings[i]->xid == xid_lookup)
+	{
+	  bind->server_used = pings[i]->server_nr;
+	  bind->current_ep = pings[i]->server_ep;
+	  found = 1;
+	}
+    }
+
+  auth_destroy (clnt->cl_auth);
+  clnt_destroy (clnt);
+  close (sock);
+
+  for (i = 0; i < pings_count; ++i)
+    free (pings[i]);
+  free (pings);
+
+  return found;
+}
diff --git a/nis/nis_intern.h b/nis/nis_intern.h
index a7cb785e40..fdc392ad7f 100644
--- a/nis/nis_intern.h
+++ b/nis/nis_intern.h
@@ -24,12 +24,32 @@
 
 __BEGIN_DECLS
 
+struct dir_binding
+{
+  CLIENT *clnt;                  /* RPC CLIENT handle */
+  nis_server *server_val;        /* List of servers */
+  u_int server_len;              /* # of servers */
+  u_int server_used;             /* Which server we are bind in the moment ? */
+  u_int current_ep;              /* Which endpoint of the server are in use? */
+  u_int trys;                    /* How many server have we tried ? */
+  bool_t master_only;            /* Is only binded to the master */
+  bool_t use_auth;               /* Do we use AUTH ? */
+  bool_t use_udp;                /* Do we use UDP ? */
+  time_t create;                 /* Binding creation time */
+  struct sockaddr_in addr;       /* Server's IP address */
+  int socket;                    /* Server's local socket */
+  unsigned short port;           /* Local port */
+};
+typedef struct dir_binding dir_binding;
+
+extern unsigned long inetstr2int __P ((const char *str));
+extern long __nis_findfastest __P ((dir_binding *bind));
 extern nis_error __do_niscall2 __P ((const nis_server *serv, u_int serv_len,
 				     u_long prog, xdrproc_t xargs, caddr_t req,
-				     xdrproc_t xres, caddr_t resp, 
+				     xdrproc_t xres, caddr_t resp,
 				     u_long flags));
 extern nis_error __do_niscall __P ((const_nis_name name, u_long prog,
-				    xdrproc_t xargs, caddr_t req, 
+				    xdrproc_t xargs, caddr_t req,
 				    xdrproc_t xres, caddr_t resp,
 				    u_long flags));
 #if defined (HAVE_SECURE_RPC)
diff --git a/nis/nis_lookup.c b/nis/nis_lookup.c
index 82db7b0b25..6224b1f2c4 100644
--- a/nis/nis_lookup.c
+++ b/nis/nis_lookup.c
@@ -96,6 +96,10 @@ nis_lookup (const_nis_name name, const u_long flags)
 	  /* XXX Implement CALLBACK here ! */
 	  ++done;
 	  break;
+	case NIS_UNAVAIL:
+	  /* NIS+ is not installed, or all servers are down */
+	  ++done;
+	  break;
 	default:
 	  /* Try the next domainname if we don't follow a link */
 	  if (count_links)
diff --git a/nis/nis_ping.c b/nis/nis_ping.c
index 941adfbda4..4ec34ce4c0 100644
--- a/nis/nis_ping.c
+++ b/nis/nis_ping.c
@@ -34,7 +34,7 @@ nis_ping (const_nis_name dirname, u_long utime, const nis_object *dirobj)
 
   if (dirobj == NULL)
     {
-      res = nis_lookup (dirname, EXPAND_NAME + FOLLOW_LINKS);
+      res = nis_lookup (dirname, MASTER_ONLY);
       if (res->status != NIS_SUCCESS && res->status != NIS_S_SUCCESS)
 	return;
       obj = res->objects.objects_val;
@@ -44,7 +44,11 @@ nis_ping (const_nis_name dirname, u_long utime, const nis_object *dirobj)
 
   /* Check if obj is really a diryectory object */
   if (obj->zo_data.zo_type != DIRECTORY_OBJ)
-    abort ();
+    {
+      if (res != NULL)
+	nis_freeresult (res);
+      return;
+    }
 
   if (dirname == NULL)
     args.dir = obj->DI_data.do_name;
diff --git a/nis/nis_table.c b/nis/nis_table.c
index 16fd1e68ec..a3bfa2c0cc 100644
--- a/nis/nis_table.c
+++ b/nis/nis_table.c
@@ -248,7 +248,11 @@ nis_list (const_nis_name name, u_long flags,
 	  /* XXX Implement CALLBACK here ! */
 	  ++done;
 	  break;
-	default:
+        case NIS_UNAVAIL:  
+          /* NIS+ is not installed, or all servers are down */
+          ++done;
+          break;
+        default:
 	  /* Try the next domainname if we don't follow a link */
 	  if (count_links)
 	    {
diff --git a/nis/nis_util.c b/nis/nis_util.c
index 4e39d6fb6a..b6eef9b227 100644
--- a/nis/nis_util.c
+++ b/nis/nis_util.c
@@ -24,6 +24,7 @@
 fd_result *
 __nis_finddirectory (directory_obj *dir, const_nis_name name)
 {
+  nis_error status;
   fd_args fd_args;
   fd_result *fd_res;
 
@@ -31,13 +32,14 @@ __nis_finddirectory (directory_obj *dir, const_nis_name name)
   fd_args.requester = nis_local_host();
   fd_res = calloc (1, sizeof (fd_result));
       
-  if (__do_niscall2 (dir->do_servers.do_servers_val, 
-		     dir->do_servers.do_servers_len, NIS_FINDDIRECTORY,
-		     (xdrproc_t) xdr_fd_args,
-		     (caddr_t) &fd_args, (xdrproc_t) xdr_fd_result,
-		     (caddr_t) fd_res, NO_AUTHINFO|USE_DGRAM) != NIS_SUCCESS)
-    fd_res->status = NIS_RPCERROR;
-  
+  if ((status = __do_niscall2 (dir->do_servers.do_servers_val, 
+			       dir->do_servers.do_servers_len, 
+			       NIS_FINDDIRECTORY, (xdrproc_t) xdr_fd_args,
+			       (caddr_t) &fd_args, (xdrproc_t) xdr_fd_result,
+			       (caddr_t) fd_res, 
+			       NO_AUTHINFO|USE_DGRAM)) != NIS_SUCCESS)
+    fd_res->status = status;
+      
   return fd_res;
 }
 
diff --git a/nis/nss_compat/compat-grp.c b/nis/nss_compat/compat-grp.c
index 9726784b2d..35ae2f7b02 100644
--- a/nis/nss_compat/compat-grp.c
+++ b/nis/nss_compat/compat-grp.c
@@ -18,6 +18,7 @@
    Boston, MA 02111-1307, USA.  */
 
 #include <errno.h>
+#include <fcntl.h>
 #include <nss.h>
 #include <grp.h>
 #include <ctype.h>
@@ -132,6 +133,26 @@ internal_setgrent (ent_t *ent)
 
       if (ent->stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
+      else
+	{
+	  /* We have to make sure the file is  `closed on exec'.  */
+	  int result, flags;
+
+	  result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
+	  if (result >= 0)
+	    {
+	      flags |= FD_CLOEXEC;
+	      result = fcntl (fileno (ent->stream), F_SETFD, flags);
+	    }
+	  if (result < 0)
+	    {
+	      /* Something went wrong.  Close the stream and return a
+		 failure.  */
+	      fclose (ent->stream);
+	      ent->stream = NULL;
+	      status = NSS_STATUS_UNAVAIL;
+	    }
+	}
     }
   else
     rewind (ent->stream);
@@ -277,7 +298,7 @@ getgrent_next_nis (struct group *result, ent_t *ent, char *buffer,
 	  if (!save_nis_first)
 	    free (save_oldkey);
 	}
-      
+
       if (parse_res &&
 	  in_blacklist (result->gr_name, strlen (result->gr_name), ent))
 	parse_res = 0; /* if result->gr_name in blacklist,search next entry */
@@ -297,7 +318,7 @@ getgrent_next_nisplus (struct group *result, ent_t *ent, char *buffer,
     {
       nis_result *save_oldres;
       bool_t save_nis_first;
-      
+
       if (ent->nis_first)
         {
 	  save_oldres = ent->result;
@@ -324,7 +345,7 @@ getgrent_next_nisplus (struct group *result, ent_t *ent, char *buffer,
 	      return niserr2nss (ent->result->status);
             }
         }
-      if ((parse_res = _nss_nisplus_parse_grent (ent->result, 0, result, 
+      if ((parse_res = _nss_nisplus_parse_grent (ent->result, 0, result,
 						 buffer, buflen)) == -1)
 	{
 	  nis_freeresult (ent->result);
@@ -370,7 +391,7 @@ getgrent_next_file_plusgroup (struct group *result, char *buffer,
           nis_freeresult (res);
           return status;
         }
-      if ((parse_res = _nss_nisplus_parse_grent (res, 0, result, buffer, 
+      if ((parse_res = _nss_nisplus_parse_grent (res, 0, result, buffer,
 						 buflen)) == -1)
 	{
 	  __set_errno (ERANGE);
diff --git a/nis/nss_compat/compat-pwd.c b/nis/nss_compat/compat-pwd.c
index af1267922c..6fac0f46c6 100644
--- a/nis/nss_compat/compat-pwd.c
+++ b/nis/nss_compat/compat-pwd.c
@@ -21,6 +21,7 @@
 #include <pwd.h>
 #include <errno.h>
 #include <ctype.h>
+#include <fcntl.h>
 #include <netdb.h>
 #include <string.h>
 #include <bits/libc-lock.h>
@@ -232,6 +233,26 @@ internal_setpwent (ent_t *ent)
 
       if (ent->stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
+      else
+	{
+	  /* We have to make sure the file is  `closed on exec'.  */
+	  int result, flags;
+
+	  result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
+	  if (result >= 0)
+	    {
+	      flags |= FD_CLOEXEC;
+	      result = fcntl (fileno (ent->stream), F_SETFD, flags);
+	    }
+	  if (result < 0)
+	    {
+	      /* Something went wrong.  Close the stream and return a
+		 failure.  */
+	      fclose (ent->stream);
+	      ent->stream = NULL;
+	      status = NSS_STATUS_UNAVAIL;
+	    }
+	}
     }
   else
     rewind (ent->stream);
@@ -274,7 +295,7 @@ internal_endpwent (ent_t *ent)
 
   if (ent->netgroup)
     __internal_endnetgrent (&ent->netgrdata);
-  
+
   ent->nis = ent->first = ent->netgroup = 0;
 
   if (ent->oldkey != NULL)
@@ -432,13 +453,13 @@ getpwent_next_nisplus_netgr (struct passwd *result, ent_t *ent, char *group,
 	  give_pwd_free (&ent->pwd);
 	  return NSS_STATUS_RETURN;
 	}
-      
+
       if (user == NULL || user[0] == '-')
 	continue;
-      
+
       if (domain != NULL && strcmp (ypdomain, domain) != 0)
 	continue;
-      
+
       p2len = pwd_need_buflen (&ent->pwd);
       if (p2len > buflen)
 	{
@@ -457,7 +478,7 @@ getpwent_next_nisplus_netgr (struct passwd *result, ent_t *ent, char *group,
 	  nis_freeresult (nisres);
 	  continue;
 	}
-      if ((parse_res = _nss_nisplus_parse_pwent (nisres, result, buffer, 
+      if ((parse_res = _nss_nisplus_parse_pwent (nisres, result, buffer,
 						 buflen)) == -1)
 	{
 	  nis_freeresult (nisres);
@@ -551,7 +572,7 @@ getpwent_next_nisplus (struct passwd *result, ent_t *ent, char *buffer,
 	  if (!saved_first)
 	    nis_freeresult (saved_res);
 	}
-      
+
       if (parse_res &&
 	  in_blacklist (result->pw_name, strlen (result->pw_name), ent))
 	parse_res = 0; /* if result->pw_name in blacklist,search next entry */
@@ -592,7 +613,7 @@ getpwent_next_nis (struct passwd *result, ent_t *ent, char *buffer,
       bool_t saved_first;
       char *saved_oldkey;
       int saved_oldlen;
-      
+
       if (ent->first)
 	{
 	  if (yp_first (domain, "passwd.byname", &outkey, &outkeylen,
@@ -602,7 +623,7 @@ getpwent_next_nis (struct passwd *result, ent_t *ent, char *buffer,
 	      give_pwd_free (&ent->pwd);
 	      return NSS_STATUS_UNAVAIL;
 	    }
-	  
+
 	  saved_first = TRUE;
 	  saved_oldkey = ent->oldkey;
 	  saved_oldlen = ent->oldkeylen;
@@ -699,7 +720,7 @@ getpwent_next_file_plususer (struct passwd *result, char *buffer,
 	  nis_freeresult (res);
 	  return status;
 	}
-      if ((parse_res = _nss_nisplus_parse_pwent (res, result, buffer, 
+      if ((parse_res = _nss_nisplus_parse_pwent (res, result, buffer,
 						 buflen)) == -1)
 	{
 	  nis_freeresult (res);
@@ -713,10 +734,10 @@ getpwent_next_file_plususer (struct passwd *result, char *buffer,
       char *domain;
       char *outval;
       int outvallen;
-      
+
       if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
 	return NSS_STATUS_TRYAGAIN;
-      
+
       if (yp_match (domain, "passwd.byname", &result->pw_name[1],
 		    strlen (result->pw_name) - 1, &outval, &outvallen)
 	  != YPERR_SUCCESS)
diff --git a/nis/nss_compat/compat-spwd.c b/nis/nss_compat/compat-spwd.c
index 61a703c1ea..a4c6201c2d 100644
--- a/nis/nss_compat/compat-spwd.c
+++ b/nis/nss_compat/compat-spwd.c
@@ -20,6 +20,7 @@
 #include <nss.h>
 #include <errno.h>
 #include <ctype.h>
+#include <fcntl.h>
 #include <netdb.h>
 #include <shadow.h>
 #include <string.h>
@@ -184,6 +185,26 @@ internal_setspent (ent_t *ent)
 
       if (ent->stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
+      else
+	{
+	  /* We have to make sure the file is  `closed on exec'.  */
+	  int result, flags;
+
+	  result = flags = fcntl (fileno (ent->stream), F_GETFD, 0);
+	  if (result >= 0)
+	    {
+	      flags |= FD_CLOEXEC;
+	      result = fcntl (fileno (ent->stream), F_SETFD, flags);
+	    }
+	  if (result < 0)
+	    {
+	      /* Something went wrong.  Close the stream and return a
+		 failure.  */
+	      fclose (ent->stream);
+	      ent->stream = NULL;
+	      status = NSS_STATUS_UNAVAIL;
+	    }
+	}
     }
   else
     rewind (ent->stream);
@@ -305,10 +326,10 @@ getspent_next_nis_netgr (struct spwd *result, ent_t *ent, char *group,
 	  give_spwd_free (&ent->pwd);
 	  return NSS_STATUS_RETURN;
 	}
-      
+
       if (user == NULL || user[0] == '-')
 	continue;
-      
+
       if (domain != NULL && strcmp (ypdomain, domain) != 0)
 	continue;
 
@@ -651,7 +672,7 @@ getspent_next_file_plususer (struct spwd *result, char *buffer,
           nis_freeresult (res);
           return status;
         }
-      if ((parse_res = _nss_nisplus_parse_spent (res, result, buffer, 
+      if ((parse_res = _nss_nisplus_parse_spent (res, result, buffer,
 						 buflen)) == -1)
 	{
 	  nis_freeresult (res);
@@ -735,7 +756,7 @@ getspent_next_file (struct spwd *result, ent_t *ent,
           __set_errno (ERANGE);
           return NSS_STATUS_TRYAGAIN;
         }
-      
+
       if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-')
 	/* This is a real entry.  */
 	break;
diff --git a/nis/ypclnt.c b/nis/ypclnt.c
index 85597144b2..7c9efec70a 100644
--- a/nis/ypclnt.c
+++ b/nis/ypclnt.c
@@ -130,12 +130,15 @@ __yp_bind (const char *domain, dom_binding ** ypdb)
                          (caddr_t) &ypbr, TIMEOUT) != RPC_SUCCESS)
             {
               clnt_destroy (client);
+	      close (clnt_sock);
               if (is_new)
                 free (ysd);
               return YPERR_YPBIND;
             }
 
           clnt_destroy (client);
+	  close (clnt_sock);
+
           if (ypbr.ypbind_status != YPBIND_SUCC_VAL)
             {
               switch (ypbr.ypbind_resp_u.ypbind_error)
@@ -174,7 +177,10 @@ __yp_bind (const char *domain, dom_binding ** ypdb)
         }
 
       if (ysd->dom_client)
-        clnt_destroy (ysd->dom_client);
+	{
+	  clnt_destroy (ysd->dom_client);
+	  close (ysd->dom_socket);
+	}
       ysd->dom_socket = RPC_ANYSOCK;
       ysd->dom_client = clntudp_create (&ysd->dom_server_addr, YPPROG, YPVERS,
                                         TIMEOUT, &ysd->dom_socket);
@@ -635,18 +641,16 @@ yp_all (const char *indomain, const char *inmap,
 			  (caddr_t) &req, (xdrproc_t) __xdr_ypresp_all,
 			  (caddr_t) &status, TIMEOUT);
 
+      clnt_destroy (clnt);
+      close (clnt_sock);
       if (result != RPC_SUCCESS)
 	{
 	  clnt_perror (ydb->dom_client, "yp_all: clnt_call");
-	  clnt_destroy (clnt);
 	  __yp_unbind (ydb);
 	  result = YPERR_RPC;
 	}
       else
-	{
-	  clnt_destroy (clnt);
-	  result = YPERR_SUCCESS;
-	}
+	result = YPERR_SUCCESS;
 
       __libc_lock_unlock (ypbindlist_lock);