This is the mail archive of the libc-hacker@sourceware.cygnus.com mailing list for the glibc project.

Note that libc-hacker is a closed list. You may look at the archives of this list, but subscription and posting are not open.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[PATCH] NIS caching and errno handling



Hi,

I have appended 2 patches, one for glibc 2.1 and one for glibc 2.2.

I hope now always errno is set correct in the NSS modules.
And I have correct the handling of the cached RPC client handles.

  Thorsten

-- 
Thorsten Kukuk       http://www.suse.de/~kukuk/       kukuk@suse.de
SuSE GmbH            Schanzaeckerstr. 10            90443 Nuernberg
Linux is like a Vorlon.  It is incredibly powerful, gives terse,
cryptic answers and has a lot of things going on in the background.
1999-12-14  Thorsten Kukuk  <kukuk@suse.de>

	* nis/ypclnt.c: Correct handling of cached client handles.
	* nis/nss_compat/compat-grp.c: Make sure errno is always set correct.
	* nis/nss_compat/compat-initgroups.c: Likewise.
	* nis/nss_compat/compat-spwd.c: Likewise.
	* nis/nss_nis/nis-alias.c: Likewise.
	* nis/nss_nis/nis-ethers.c: Likewise.
	* nis/nss_nis/nis-grp.c: Likewise.
	* nis/nss_nis/nis-hosts.c: Likewise.
	* nis/nss_nis/nis-netgrp.c: Likewise.
	* nis/nss_nis/nis-publickey.c: Likewise.
	* nis/nss_nis/nis-service.c: Likewise. Also use services.byservicename
	  Map if available, optimize query if name/port and protocol is known.

diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/nss_compat/compat-grp.c glibc-2.1/nis/nss_compat/compat-grp.c
--- ../src/glibc-2.1/nis/nss_compat/compat-grp.c	Sat Sep 25 20:23:35 1999
+++ glibc-2.1/nis/nss_compat/compat-grp.c	Sun Dec 12 15:07:26 1999
@@ -1,6 +1,6 @@
 /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -730,7 +730,10 @@
   enum nss_status status;
 
   if (name[0] == '-' || name[0] == '+')
-    return NSS_STATUS_NOTFOUND;
+    {
+      *errnop = ENOENT;
+      return NSS_STATUS_NOTFOUND;
+    }
 
   __libc_lock_lock (lock);
 
@@ -915,7 +918,10 @@
 
 	  status = getgrgid_plusgroup (gid, result, buffer, buflen, errnop);
 	  if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
-	    return NSS_STATUS_NOTFOUND;
+	    {
+	      *errnop = ENOENT;
+	      return NSS_STATUS_NOTFOUND;
+	    }
 	  else
 	    return status;
 	}
diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/nss_compat/compat-initgroups.c glibc-2.1/nis/nss_compat/compat-initgroups.c
--- ../src/glibc-2.1/nis/nss_compat/compat-initgroups.c	Mon Aug  2 08:56:12 1999
+++ glibc-2.1/nis/nss_compat/compat-initgroups.c	Sun Dec 12 15:14:22 1999
@@ -126,7 +126,7 @@
 
       grptable = malloc (sizeof (key) + len_local_dir);
       if (grptable == NULL)
-        return NSS_STATUS_TRYAGAIN;
+	return NSS_STATUS_TRYAGAIN;
 
       grptablelen = ((char *) mempcpy (mempcpy (grptable,
 						key, sizeof (key) - 1),
@@ -263,6 +263,7 @@
   if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
     {
       ent->nis = 0;
+      *errnop = ENOENT;
       return NSS_STATUS_NOTFOUND;
     }
 
@@ -445,7 +446,10 @@
         ++p;
       parse_res = _nss_files_parse_grent (p, result, data, buflen, errnop);
       if (parse_res == -1)
-	return NSS_STATUS_TRYAGAIN;
+	{
+	  *errnop = ERANGE;
+	  return NSS_STATUS_TRYAGAIN;
+	}
     }
 
   if (parse_res)
diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/nss_compat/compat-spwd.c glibc-2.1/nis/nss_compat/compat-spwd.c
--- ../src/glibc-2.1/nis/nss_compat/compat-spwd.c	Fri Feb 19 09:33:38 1999
+++ glibc-2.1/nis/nss_compat/compat-spwd.c	Sun Dec 12 15:17:29 1999
@@ -383,6 +383,7 @@
       if (parse_res == -1)
 	{
 	  ent->netgrdata.cursor = saved_cursor;
+	  *errnop = ERANGE;
 	  return NSS_STATUS_TRYAGAIN;
 	}
 
@@ -1112,7 +1113,10 @@
 	  && result->sp_namp[1] != '@')
 	{
 	  if (strcmp (&result->sp_namp[1], name) == 0)
-	    return NSS_STATUS_NOTFOUND;
+	    {
+	      *errnop = ENOENT;
+	      return NSS_STATUS_NOTFOUND;
+	    }
 	  else
 	    continue;
 	}
@@ -1128,8 +1132,11 @@
 	      status = getspnam_plususer (name, result, buffer, buflen,
 					  errnop);
 	      if (status == NSS_STATUS_RETURN)
-		/* We couldn't parse the entry */
-		return NSS_STATUS_NOTFOUND;
+		{
+		  /* We couldn't parse the entry */
+		  *errnop = ENOENT;
+		  return NSS_STATUS_NOTFOUND;
+		}
 	      else
 		return status;
 	    }
@@ -1142,7 +1149,10 @@
 
 	  status = getspnam_plususer (name, result, buffer, buflen, errnop);
 	  if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
-	    return NSS_STATUS_NOTFOUND;
+	    {
+	      *errnop = ENOENT;
+	      return NSS_STATUS_NOTFOUND;
+	    }
 	  else
 	    return status;
 	}
@@ -1159,7 +1169,10 @@
   enum nss_status status;
 
   if (name[0] == '-' || name[0] == '+')
-    return NSS_STATUS_NOTFOUND;
+    {
+      *errnop = ENOENT;
+      return NSS_STATUS_NOTFOUND;
+    }
 
   if (ni == NULL)
     {
diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/nss_nis/nis-alias.c glibc-2.1/nis/nss_nis/nis-alias.c
--- ../src/glibc-2.1/nis/nss_nis/nis-alias.c	Sat Jan 23 23:13:41 1999
+++ glibc-2.1/nis/nss_nis/nis-alias.c	Sun Dec 12 11:17:02 1999
@@ -278,7 +278,10 @@
       if (parse_res == -1)
 	return NSS_STATUS_TRYAGAIN;
       else
-	return NSS_STATUS_NOTFOUND;
+	{
+	  *errnop = ENOENT;
+	  return NSS_STATUS_NOTFOUND;
+	}
     }
 
   return NSS_STATUS_SUCCESS;
diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/nss_nis/nis-ethers.c glibc-2.1/nis/nss_nis/nis-ethers.c
--- ../src/glibc-2.1/nis/nss_nis/nis-ethers.c	Wed Aug 19 01:24:46 1998
+++ glibc-2.1/nis/nss_nis/nis-ethers.c	Sun Dec 12 11:21:39 1999
@@ -1,6 +1,6 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -241,7 +241,10 @@
       if (parse_res == -1)
 	return NSS_STATUS_TRYAGAIN;
       else
-	return NSS_STATUS_NOTFOUND;
+	{
+	  *errnop = ENOENT;
+	  return NSS_STATUS_NOTFOUND;
+	}
     }
   return NSS_STATUS_SUCCESS;
 }
diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/nss_nis/nis-grp.c glibc-2.1/nis/nss_nis/nis-grp.c
--- ../src/glibc-2.1/nis/nss_nis/nis-grp.c	Wed Aug 19 01:24:47 1998
+++ glibc-2.1/nis/nss_nis/nis-grp.c	Sun Dec 12 11:26:13 1999
@@ -1,6 +1,6 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -207,7 +207,7 @@
 	return NSS_STATUS_TRYAGAIN;
       else
 	{
-	  *errnop = EAGAIN;
+	  *errnop = ENOENT;
 	  return NSS_STATUS_NOTFOUND;
 	}
     }
diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/nss_nis/nis-hosts.c glibc-2.1/nis/nss_nis/nis-hosts.c
--- ../src/glibc-2.1/nis/nss_nis/nis-hosts.c	Wed Aug 19 01:24:48 1998
+++ glibc-2.1/nis/nss_nis/nis-hosts.c	Sun Dec 12 11:30:46 1999
@@ -1,6 +1,6 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -314,6 +314,7 @@
       else
 	{
 	  *h_errnop = HOST_NOT_FOUND;
+	  *errnop = ENOENT;
 	  return NSS_STATUS_NOTFOUND;
 	}
     }
@@ -375,7 +376,10 @@
 	  *errnop = errno;
 	}
       if (retval == NSS_STATUS_NOTFOUND)
-	*h_errnop = HOST_NOT_FOUND;
+	{
+	  *h_errnop = HOST_NOT_FOUND;
+	  *errnop = ENOENT;
+	}
       return retval;
     }
 
@@ -404,6 +408,7 @@
       else
 	{
 	  *h_errnop = HOST_NOT_FOUND;
+	  *errnop = ENOENT;
 	  return NSS_STATUS_NOTFOUND;
 	}
     }
diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/nss_nis/nis-netgrp.c glibc-2.1/nis/nss_nis/nis-netgrp.c
--- ../src/glibc-2.1/nis/nss_nis/nis-netgrp.c	Mon Dec  8 03:39:59 1997
+++ glibc-2.1/nis/nss_nis/nis-netgrp.c	Sun Dec 12 11:34:51 1999
@@ -1,6 +1,6 @@
-/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -117,7 +117,10 @@
   enum nss_status status;
 
   if (cursor == NULL)
-    return NSS_STATUS_NOTFOUND;
+    {
+      *errnop = ENOENT;
+      return NSS_STATUS_NOTFOUND;
+    }
 
   __libc_lock_lock (lock);
 
diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/nss_nis/nis-publickey.c glibc-2.1/nis/nss_nis/nis-publickey.c
--- ../src/glibc-2.1/nis/nss_nis/nis-publickey.c	Wed Aug 19 01:24:52 1998
+++ glibc-2.1/nis/nss_nis/nis-publickey.c	Sun Dec 12 11:48:47 1999
@@ -1,6 +1,6 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -50,8 +50,11 @@
 
   domain = strchr (netname, '@');
   if (!domain)
-    return NSS_STATUS_UNAVAIL;
-  domain++;
+    {
+      *errnop = EINVAL;
+      return NSS_STATUS_UNAVAIL;
+    }
+  ++domain;
 
   retval = yperr2nss (yp_match (domain, "publickey.byname", netname,
 				strlen (netname), &result, &len));
@@ -95,8 +98,11 @@
 
   domain = strchr (netname, '@');
   if (!domain)
-    return NSS_STATUS_UNAVAIL;
-  domain++;
+    {
+      *errnop = EINVAL;
+      return NSS_STATUS_UNAVAIL;
+    }
+  ++domain;
 
   retval = yperr2nss (yp_match (domain, "publickey.byname", netname,
 				strlen (netname), &result, &len));
@@ -196,10 +202,13 @@
 
   domain = strchr (netname, '@');
   if (!domain)
-    return NSS_STATUS_UNAVAIL;
+    {
+      *errnop = EINVAL;
+      return NSS_STATUS_UNAVAIL;
+    }
 
   /* Point past the '@' character */
-  domain++;
+  ++domain;
   lookup = NULL;
   yperr = yp_match (domain, "netid.byname", netname, strlen (netname),
 		    &lookup, &len);
@@ -209,11 +218,13 @@
       break;			/* the successful case */
     case YPERR_DOMAIN:
     case YPERR_KEY:
+      *errnop = ENOENT;
       return NSS_STATUS_NOTFOUND;
     case YPERR_MAP:
     default:
       return NSS_STATUS_UNAVAIL;
     }
+
   if (lookup)
     {
       enum nss_status err;
@@ -224,7 +235,9 @@
       return err;
     }
   else
-    return NSS_STATUS_NOTFOUND;
-
+    {
+      *errnop = ENOENT;
+      return NSS_STATUS_NOTFOUND;
+    }
   return NSS_STATUS_SUCCESS;
 }
diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/nss_nis/nis-service.c glibc-2.1/nis/nss_nis/nis-service.c
--- ../src/glibc-2.1/nis/nss_nis/nis-service.c	Thu Aug  5 11:21:09 1999
+++ glibc-2.1/nis/nss_nis/nis-service.c	Sun Dec 12 15:08:23 1999
@@ -172,7 +172,7 @@
 	  return NSS_STATUS_NOTFOUND;
 	}
       p = strncpy (buffer, data->next->val, buflen);
-           while (isspace (*p))
+      while (isspace (*p))
         ++p;
 
       parse_res = _nss_files_parse_servent (p, serv, pdata, buflen, errnop);
@@ -215,6 +215,63 @@
       return NSS_STATUS_UNAVAIL;
     }
 
+  /* If the protocol is given, we could try if our NIS server knows
+     about services.byservicename map. If yes, we only need one query */
+  if (protocol != NULL)
+    {
+      char key[strlen (name) + strlen (protocol) + 2];
+      char *cp, *domain, *result;
+      size_t keylen, len;
+
+      /* If this fails, the other solution will also fail. */
+      if (yp_get_default_domain (&domain))
+	return NSS_STATUS_UNAVAIL;
+
+      /* key is: "name/protocol" */
+      cp = stpcpy (key, name);
+      *cp++ = '/';
+      stpcpy (cp, protocol);
+      keylen = strlen (key);
+      status = yperr2nss (yp_match (domain, "services.byservicename", key,
+				    keylen, &result, &len));
+
+      /* If we found the key, it's ok and parse the result. If not,
+	 fall through and parse the complete table. */
+      if (status == NSS_STATUS_SUCCESS)
+	{
+	  struct parser_data *pdata = (void *) buffer;
+	  int parse_res;
+	  char *p;
+
+	  if ((size_t) (len + 1) > buflen)
+	    {
+	      free (result);
+	      *errnop = ERANGE;
+	      return NSS_STATUS_TRYAGAIN;
+	    }
+
+	  p = strncpy (buffer, result, len);
+	  buffer[len] = '\0';
+	  while (isspace (*p))
+	    ++p;
+	  free (result);
+	  parse_res = _nss_files_parse_servent (p, serv, pdata,
+						buflen, errnop);
+	  if (parse_res < 0)
+	    {
+	      if (parse_res == -1)
+		return NSS_STATUS_TRYAGAIN;
+	      else
+		{
+		  *errnop = ENOENT;
+		  return NSS_STATUS_NOTFOUND;
+		}
+	    }
+	  else
+	    return NSS_STATUS_SUCCESS;
+	}
+    }
+
   status = internal_nis_setservent (&data);
   if (status != NSS_STATUS_SUCCESS)
     return status;
@@ -256,10 +313,57 @@
   enum nss_status status;
   int found;
 
-  if (protocol == NULL)
+  /* If the protocol is given, we only need one query */
+  if (protocol != NULL)
     {
-      *errnop = EINVAL;
-      return NSS_STATUS_UNAVAIL;
+      char key[100 + strlen (protocol) + 2];
+      char *domain, *result;
+      size_t keylen, len;
+
+      /* If this fails, the other solution will also fail. */
+      if (yp_get_default_domain (&domain))
+	return NSS_STATUS_UNAVAIL;
+
+      /* key is: "port/protocol" */
+      keylen = snprintf (key, sizeof (key), "%d/%s", port, protocol);
+      status = yperr2nss (yp_match (domain, "services.byname", key,
+				    keylen, &result, &len));
+
+      /* If we found the key, it's ok and parse the result. If not,
+	 fall through and parse the complete table. */
+      if (status == NSS_STATUS_SUCCESS)
+	{
+	  struct parser_data *pdata = (void *) buffer;
+	  int parse_res;
+	  char *p;
+
+	  if ((size_t) (len + 1) > buflen)
+	    {
+	      free (result);
+	      *errnop = ERANGE;
+	      return NSS_STATUS_TRYAGAIN;
+	    }
+
+	  p = strncpy (buffer, result, len);
+	  buffer[len] = '\0';
+	  while (isspace (*p))
+	    ++p;
+	  free (result);
+	  parse_res = _nss_files_parse_servent (p, serv, pdata,
+						buflen, errnop);
+	  if (parse_res < 0)
+	    {
+	      if (parse_res == -1)
+		return NSS_STATUS_TRYAGAIN;
+	      else
+		{
+		  *errnop = ENOENT;
+		  return NSS_STATUS_NOTFOUND;
+		}
+	    }
+	  else
+	    return NSS_STATUS_SUCCESS;
+	}
     }
 
   status = internal_nis_setservent (&data);
@@ -270,7 +374,8 @@
   while (!found &&
          ((status = internal_nis_getservent_r (serv, buffer, buflen, errnop,
 					       &data)) == NSS_STATUS_SUCCESS))
-    if (serv->s_port == port &&  strcmp (serv->s_proto, protocol) == 0)
+    if (serv->s_port == port &&
+	(protocol == NULL || strcmp (serv->s_proto, protocol) == 0))
       found = 1;
 
   internal_nis_endservent (&data);
diff -u -r --new-file --exclude=CVS ../src/glibc-2.1/nis/ypclnt.c glibc-2.1/nis/ypclnt.c
--- ../src/glibc-2.1/nis/ypclnt.c	Sun Nov 21 09:54:10 1999
+++ glibc-2.1/nis/ypclnt.c	Sun Dec 12 11:06:35 1999
@@ -1,6 +1,6 @@
 /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -41,7 +41,6 @@
     struct sockaddr_in dom_server_addr;
     int dom_socket;
     CLIENT *dom_client;
-    long int dom_vers;
   };
 typedef struct dom_binding dom_binding;
 
@@ -62,7 +61,6 @@
   int clnt_sock;
   CLIENT *client;
   int is_new = 0;
-  int try;
 
   if (domain == NULL || domain[0] == '\0')
     return YPERR_BADARGS;
@@ -82,137 +80,127 @@
     {
       is_new = 1;
       ysd = (dom_binding *) calloc (1, sizeof *ysd);
-      ysd->dom_socket = -1;
-      ysd->dom_vers = -1;
     }
 
-  try = 0;
-
-  do
-    {
-      ++try;
-      if (try > MAXTRIES)
-        {
-          if (is_new)
-            free (ysd);
-          return YPERR_YPBIND;
-        }
-
 #if USE_BINDINGDIR
-      if (ysd->dom_vers < 1 && try == 1) /* Try binding dir only first time */
+  if (ysd->dom_client == NULL)
+    {
+      /* Try binding dir at first if we have no binding */
+      char path[sizeof (BINDINGDIR) + strlen (domain) + 10];
+      struct iovec vec[2];
+      unsigned short port;
+      int fd;
+
+      sprintf (path, "%s/%s.%d", BINDINGDIR, domain, YPBINDVERS);
+      fd = open (path, O_RDONLY);
+      if (fd >= 0)
 	{
-	  char path[sizeof (BINDINGDIR) - 1 + strlen (domain) + 10];
-	  struct iovec vec[2];
-	  unsigned short port;
-	  int fd;
-
-	  sprintf (path, "%s/%s.%d", BINDINGDIR, domain, YPBINDVERS);
-	  fd = open (path, O_RDONLY);
-	  if (fd >= 0)
+	  /* We have a binding file and could save a RPC call */
+	  vec[0].iov_base = &port;
+	  vec[0].iov_len = sizeof (port);
+	  vec[1].iov_base = &ypbr;
+	  vec[1].iov_len = sizeof (ypbr);
+
+	  if (readv (fd, vec, 2) == sizeof (port) + sizeof (ypbr))
 	    {
-	      /* We have a binding file and could save a RPC call */
-	      vec[0].iov_base = &port;
-	      vec[0].iov_len = sizeof (port);
-	      vec[1].iov_base = &ypbr;
-	      vec[1].iov_len = sizeof (ypbr);
-
-	      if (readv (fd, vec, 2) == sizeof (port) + sizeof (ypbr))
-		{
-		  ysd->dom_server_addr.sin_family = AF_INET;
-		  memcpy (&ysd->dom_server_addr.sin_port,
-			  ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
-			  sizeof (ysd->dom_server_addr.sin_port));
-		  memcpy (&ysd->dom_server_addr.sin_addr.s_addr,
-			  ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
-			  sizeof (ysd->dom_server_addr.sin_addr.s_addr));
-		  ysd->dom_vers = YPVERS;
-		  strncpy (ysd->dom_domain, domain, YPMAXDOMAIN);
-		  ysd->dom_domain[YPMAXDOMAIN] = '\0';
-		}
-	      close (fd);
+	      ysd->dom_server_addr.sin_family = AF_INET;
+	      memcpy (&ysd->dom_server_addr.sin_port,
+		      ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
+		      sizeof (ysd->dom_server_addr.sin_port));
+	      memcpy (&ysd->dom_server_addr.sin_addr.s_addr,
+		      ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
+		      sizeof (ysd->dom_server_addr.sin_addr.s_addr));
+	      strncpy (ysd->dom_domain, domain, YPMAXDOMAIN);
+	      ysd->dom_domain[YPMAXDOMAIN] = '\0';
+
+	      ysd->dom_socket = RPC_ANYSOCK;
+	      ysd->dom_client = clntudp_create (&ysd->dom_server_addr, YPPROG,
+						YPVERS, UDPTIMEOUT,
+						&ysd->dom_socket);
+
+	      if (ysd->dom_client != NULL)
+		/* If the program exits, close the socket */
+		if (fcntl (ysd->dom_socket, F_SETFD, 1) == -1)
+		  perror ("fcntl: F_SETFD");
 	    }
+	  close (fd);
 	}
+    }
 #endif /* USE_BINDINGDIR */
 
-      if (ysd->dom_vers == -1)
+  if (ysd->dom_client == NULL)
+    {
+      memset (&clnt_saddr, '\0', sizeof clnt_saddr);
+      clnt_saddr.sin_family = AF_INET;
+      clnt_saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+      clnt_sock = RPC_ANYSOCK;
+      client = clnttcp_create (&clnt_saddr, YPBINDPROG, YPBINDVERS,
+			       &clnt_sock, 0, 0);
+      if (client == NULL)
 	{
-	  if (ysd->dom_client)
-	    {
-	      clnt_destroy (ysd->dom_client);
-	      ysd->dom_client = NULL;
-	      ysd->dom_socket = -1;
-	    }
-          memset (&clnt_saddr, '\0', sizeof clnt_saddr);
-          clnt_saddr.sin_family = AF_INET;
-          clnt_saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
-          clnt_sock = RPC_ANYSOCK;
-          client = clnttcp_create (&clnt_saddr, YPBINDPROG, YPBINDVERS,
-                                   &clnt_sock, 0, 0);
-          if (client == NULL)
-            {
-              if (is_new)
-                free (ysd);
-              return YPERR_YPBIND;
-            }
-          /*
-          ** Check the port number -- should be < IPPORT_RESERVED.
-          ** If not, it's possible someone has registered a bogus
-          ** ypbind with the portmapper and is trying to trick us.
-          */
-          if (ntohs (clnt_saddr.sin_port) >= IPPORT_RESERVED)
-            {
-              clnt_destroy (client);
-              if (is_new)
-                free (ysd);
-              return YPERR_YPBIND;
-            }
-
-          if (clnt_call (client, YPBINDPROC_DOMAIN,
-                         (xdrproc_t) xdr_domainname, (caddr_t) &domain,
-                         (xdrproc_t) xdr_ypbind_resp,
-                         (caddr_t) &ypbr, RPCTIMEOUT) != RPC_SUCCESS)
-            {
-              clnt_destroy (client);
-              if (is_new)
-                free (ysd);
-              return YPERR_YPBIND;
-            }
-
-          clnt_destroy (client);
-
-          if (ypbr.ypbind_status != YPBIND_SUCC_VAL)
-            {
-	      fprintf (stderr, _("YPBINDPROC_DOMAIN: %s\n"),
-		       ypbinderr_string (ypbr.ypbind_resp_u.ypbind_error));
-	      if (is_new)
-		free (ysd);
-	      return YPERR_DOMAIN;
-	    }
-          memset (&ysd->dom_server_addr, '\0', sizeof ysd->dom_server_addr);
-          ysd->dom_server_addr.sin_family = AF_INET;
-          memcpy (&ysd->dom_server_addr.sin_port,
-                  ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
-                  sizeof (ysd->dom_server_addr.sin_port));
-          memcpy (&ysd->dom_server_addr.sin_addr.s_addr,
-                  ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
-                  sizeof (ysd->dom_server_addr.sin_addr.s_addr));
-          ysd->dom_vers = YPVERS;
-          strncpy (ysd->dom_domain, domain, YPMAXDOMAIN);
-	  ysd->dom_domain[YPMAXDOMAIN] = '\0';
-        }
+	  if (is_new)
+	    free (ysd);
+	  return YPERR_YPBIND;
+	}
+      /* Check the port number -- should be < IPPORT_RESERVED.
+	 If not, it's possible someone has registered a bogus
+	 ypbind with the portmapper and is trying to trick us. */
+      if (ntohs (clnt_saddr.sin_port) >= IPPORT_RESERVED)
+	{
+	  clnt_destroy (client);
+	  if (is_new)
+	    free (ysd);
+	  return YPERR_YPBIND;
+	}
+
+      if (clnt_call (client, YPBINDPROC_DOMAIN,
+		     (xdrproc_t) xdr_domainname, (caddr_t) &domain,
+		     (xdrproc_t) xdr_ypbind_resp,
+		     (caddr_t) &ypbr, RPCTIMEOUT) != RPC_SUCCESS)
+	{
+	  clnt_destroy (client);
+	  if (is_new)
+	    free (ysd);
+	  return YPERR_YPBIND;
+	}
+
+      clnt_destroy (client);
+
+      if (ypbr.ypbind_status != YPBIND_SUCC_VAL)
+	{
+	  fprintf (stderr, _("YPBINDPROC_DOMAIN: %s\n"),
+		   ypbinderr_string (ypbr.ypbind_resp_u.ypbind_error));
+	  if (is_new)
+	    free (ysd);
+	  return YPERR_DOMAIN;
+	}
+      memset (&ysd->dom_server_addr, '\0', sizeof ysd->dom_server_addr);
+      ysd->dom_server_addr.sin_family = AF_INET;
+      memcpy (&ysd->dom_server_addr.sin_port,
+	      ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
+	      sizeof (ysd->dom_server_addr.sin_port));
+      memcpy (&ysd->dom_server_addr.sin_addr.s_addr,
+	      ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
+	      sizeof (ysd->dom_server_addr.sin_addr.s_addr));
+      strncpy (ysd->dom_domain, domain, YPMAXDOMAIN);
+      ysd->dom_domain[YPMAXDOMAIN] = '\0';
 
       ysd->dom_socket = RPC_ANYSOCK;
       ysd->dom_client = clntudp_create (&ysd->dom_server_addr, YPPROG, YPVERS,
                                         UDPTIMEOUT, &ysd->dom_socket);
-      if (ysd->dom_client == NULL)
-        ysd->dom_vers = -1;
 
+      if (ysd->dom_client != NULL)
+	/* If the program exits, close the socket */
+	if (fcntl (ysd->dom_socket, F_SETFD, 1) == -1)
+	  perror ("fcntl: F_SETFD");
     }
-  while (ysd->dom_client == NULL);
 
-  /* If the program exists, close the socket */
-  if (fcntl (ysd->dom_socket, F_SETFD, 1) == -1)
-    perror ("fcntl: F_SETFD");
+  if (ysd->dom_client == NULL)
+    {
+      if (is_new)
+	free (ysd);
+      return YPERR_YPSERV;
+    }
 
   if (is_new && ypdb != NULL)
     {
@@ -228,7 +216,60 @@
 {
   clnt_destroy (ydb->dom_client);
   ydb->dom_client = NULL;
-  ydb->dom_socket = -1;
+}
+
+int
+yp_bind (const char *indomain)
+{
+  int status;
+
+  __libc_lock_lock (ypbindlist_lock);
+
+  status = __yp_bind (indomain, &__ypbindlist);
+
+  __libc_lock_unlock (ypbindlist_lock);
+
+  return status;
+}
+
+static void
+yp_unbind_locked (const char *indomain)
+{
+  dom_binding *ydbptr, *ydbptr2;
+
+  ydbptr2 = NULL;
+  ydbptr = __ypbindlist;
+
+  while (ydbptr != NULL)
+    {
+      if (strcmp (ydbptr->dom_domain, indomain) == 0)
+	{
+	  dom_binding *work;
+
+	  work = ydbptr;
+	  if (ydbptr2 == NULL)
+	    __ypbindlist = __ypbindlist->dom_pnext;
+	  else
+	    ydbptr2 = ydbptr->dom_pnext;
+	  __yp_unbind (work);
+	  free (work);
+	  break;
+	}
+      ydbptr2 = ydbptr;
+      ydbptr = ydbptr->dom_pnext;
+    }
+}
+
+void
+yp_unbind (const char *indomain)
+{
+  __libc_lock_lock (ypbindlist_lock);
+
+  yp_unbind_locked (indomain);
+
+  __libc_lock_unlock (ypbindlist_lock);
+
+  return;
 }
 
 static int
@@ -277,14 +318,26 @@
 
       if (result != RPC_SUCCESS)
 	{
-	  clnt_perror (ydb->dom_client, "do_ypcall: clnt_call");
-	  ydb->dom_vers = -1;
-	  if (!use_ypbindlist)
+	  /* Don't print the error message on the first try. It
+	     could be that we use cached data which is now invalid. */
+	  if (try != 0)
+	    clnt_perror (ydb->dom_client, "do_ypcall: clnt_call");
+
+	  if (use_ypbindlist)
+	    {
+	      /* We use ypbindlist, and the old cached data is
+		 invalid. unbind now and create a new binding */
+	      yp_unbind_locked (domain);
+	      __libc_lock_unlock (ypbindlist_lock);
+	      use_ypbindlist = FALSE;
+	    }
+	  else
 	    {
 	      __yp_unbind (ydb);
 	      free (ydb);
-	      ydb = NULL;
 	    }
+
+	  ydb = NULL;
 	  status = YPERR_RPC;
 	}
       else
@@ -310,52 +363,6 @@
   return status;
 }
 
-int
-yp_bind (const char *indomain)
-{
-  int status;
-
-  __libc_lock_lock (ypbindlist_lock);
-
-  status = __yp_bind (indomain, &__ypbindlist);
-
-  __libc_lock_unlock (ypbindlist_lock);
-
-  return status;
-}
-
-void
-yp_unbind (const char *indomain)
-{
-  dom_binding *ydbptr, *ydbptr2;
-
-  __libc_lock_lock (ypbindlist_lock);
-
-  ydbptr2 = NULL;
-  ydbptr = __ypbindlist;
-  while (ydbptr != NULL)
-    {
-      if (strcmp (ydbptr->dom_domain, indomain) == 0)
-	{
-	  dom_binding *work;
-
-	  work = ydbptr;
-	  if (ydbptr2 == NULL)
-	    __ypbindlist = __ypbindlist->dom_pnext;
-	  else
-	    ydbptr2 = ydbptr->dom_pnext;
-	  __yp_unbind (work);
-	  free (work);
-	  break;
-	}
-      ydbptr2 = ydbptr;
-      ydbptr = ydbptr->dom_pnext;
-    }
-
-  __libc_lock_unlock (ypbindlist_lock);
-
-  return;
-}
 
 __libc_lock_define_initialized (static, domainname_lock)
 
@@ -373,7 +380,7 @@
 	result = YPERR_NODOM;
       else if (strcmp (__ypdomainname, "(none)") == 0)
 	{
-	  /* If domainname is not set, some Systems will return "(none)" */
+	  /* If domainname is not set, some systems will return "(none)" */
 	  __ypdomainname[0] = '\0';
 	  result = YPERR_NODOM;
 	}
@@ -628,9 +635,10 @@
 	    int keylen = resp.ypresp_all_u.val.key.keydat_len;
 	    int vallen = resp.ypresp_all_u.val.val.valdat_len;
 
-	    /* XXX We are not allowed to modify the key and val data.
-	       But I don't know if all other code will continue to work,
-	       so we will fix this for glibc 2.2 <kukuk@suse.de> */
+	    /* We are not allowed to modify the key and val data.
+	       But we are allowed to add data behind the buffer,
+	       if we don't modify the length. So add an extra NUL
+	       character to avoid trouble with broken code. */
 	    *objp = YP_TRUE;
 	    memcpy (key, resp.ypresp_all_u.val.key.keydat_val, keylen);
 	    key[keylen] = '\0';
1999-12-14  Thorsten Kukuk  <kukuk@suse.de>

	* nis/ypclnt.c: Correct handling of cached client handles.
	  (__xdr_ypresp_all): Call callback function for errors, too, 
	  like Solaris does.
	* nis/nss_compat/compat-grp.c: Make sure errno is always set correct.
	* nis/nss_compat/compat-initgroups.c: Likewise.
	* nis/nss_compat/compat-spwd.c: Likewise.
	* nis/nss_nis/nis-alias.c: Likewise.
	* nis/nss_nis/nis-ethers.c: Likewise.
	* nis/nss_nis/nis-grp.c: Likewise.
	* nis/nss_nis/nis-hosts.c: Likewise.
	* nis/nss_nis/nis-netgrp.c: Likewise.
	* nis/nss_nis/nis-publickey.c: Likewise.
	* nis/nss_nis/nis-service.c: Likewise. Also use services.byservicename
	  Map if available, optimize query if name/port and protocol is known.

diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/nss_compat/compat-grp.c glibc-2.2/nis/nss_compat/compat-grp.c
--- ../src/glibc-2.2/nis/nss_compat/compat-grp.c	Sun Sep 19 03:36:53 1999
+++ glibc-2.2/nis/nss_compat/compat-grp.c	Sun Dec 12 15:41:54 1999
@@ -1,6 +1,6 @@
 /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -730,7 +730,10 @@
   enum nss_status status;
 
   if (name[0] == '-' || name[0] == '+')
-    return NSS_STATUS_NOTFOUND;
+    {
+      *errnop = ENOENT;
+      return NSS_STATUS_NOTFOUND;
+    }
 
   __libc_lock_lock (lock);
 
@@ -915,7 +918,10 @@
 
 	  status = getgrgid_plusgroup (gid, result, buffer, buflen, errnop);
 	  if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
-	    return NSS_STATUS_NOTFOUND;
+	    {
+	      *errnop = ENOENT;
+	      return NSS_STATUS_NOTFOUND;
+	    }
 	  else
 	    return status;
 	}
diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/nss_compat/compat-initgroups.c glibc-2.2/nis/nss_compat/compat-initgroups.c
--- ../src/glibc-2.2/nis/nss_compat/compat-initgroups.c	Thu Jul 29 17:46:23 1999
+++ glibc-2.2/nis/nss_compat/compat-initgroups.c	Sun Dec 12 15:41:54 1999
@@ -263,6 +263,7 @@
   if (yp_get_default_domain (&domain) != YPERR_SUCCESS)
     {
       ent->nis = 0;
+      *errnop = ENOENT;
       return NSS_STATUS_NOTFOUND;
     }
 
@@ -445,7 +446,10 @@
         ++p;
       parse_res = _nss_files_parse_grent (p, result, data, buflen, errnop);
       if (parse_res == -1)
-	return NSS_STATUS_TRYAGAIN;
+	{
+	  *errnop = ERANGE;
+	  return NSS_STATUS_TRYAGAIN;
+	}
     }
 
   if (parse_res)
diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/nss_compat/compat-spwd.c glibc-2.2/nis/nss_compat/compat-spwd.c
--- ../src/glibc-2.2/nis/nss_compat/compat-spwd.c	Fri Feb 19 09:33:38 1999
+++ glibc-2.2/nis/nss_compat/compat-spwd.c	Sun Dec 12 15:41:54 1999
@@ -383,6 +383,7 @@
       if (parse_res == -1)
 	{
 	  ent->netgrdata.cursor = saved_cursor;
+	  *errnop = ERANGE;
 	  return NSS_STATUS_TRYAGAIN;
 	}
 
@@ -1112,7 +1113,10 @@
 	  && result->sp_namp[1] != '@')
 	{
 	  if (strcmp (&result->sp_namp[1], name) == 0)
-	    return NSS_STATUS_NOTFOUND;
+	    {
+	      *errnop = ENOENT;
+	      return NSS_STATUS_NOTFOUND;
+	    }
 	  else
 	    continue;
 	}
@@ -1128,8 +1132,11 @@
 	      status = getspnam_plususer (name, result, buffer, buflen,
 					  errnop);
 	      if (status == NSS_STATUS_RETURN)
-		/* We couldn't parse the entry */
-		return NSS_STATUS_NOTFOUND;
+		{
+		  /* We couldn't parse the entry */
+		  *errnop = ENOENT;
+		  return NSS_STATUS_NOTFOUND;
+		}
 	      else
 		return status;
 	    }
@@ -1142,7 +1149,10 @@
 
 	  status = getspnam_plususer (name, result, buffer, buflen, errnop);
 	  if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */
-	    return NSS_STATUS_NOTFOUND;
+	    {
+	      *errnop = ENOENT;
+	      return NSS_STATUS_NOTFOUND;
+	    }
 	  else
 	    return status;
 	}
@@ -1159,7 +1169,10 @@
   enum nss_status status;
 
   if (name[0] == '-' || name[0] == '+')
-    return NSS_STATUS_NOTFOUND;
+    {
+      *errnop = ENOENT;
+      return NSS_STATUS_NOTFOUND;
+    }
 
   if (ni == NULL)
     {
diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/nss_nis/nis-alias.c glibc-2.2/nis/nss_nis/nis-alias.c
--- ../src/glibc-2.2/nis/nss_nis/nis-alias.c	Sat Jan 23 23:13:41 1999
+++ glibc-2.2/nis/nss_nis/nis-alias.c	Sun Dec 12 15:41:54 1999
@@ -278,7 +278,10 @@
       if (parse_res == -1)
 	return NSS_STATUS_TRYAGAIN;
       else
-	return NSS_STATUS_NOTFOUND;
+	{
+	  *errnop = ENOENT;
+	  return NSS_STATUS_NOTFOUND;
+	}
     }
 
   return NSS_STATUS_SUCCESS;
diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/nss_nis/nis-ethers.c glibc-2.2/nis/nss_nis/nis-ethers.c
--- ../src/glibc-2.2/nis/nss_nis/nis-ethers.c	Wed Aug 19 01:24:46 1998
+++ glibc-2.2/nis/nss_nis/nis-ethers.c	Sun Dec 12 15:41:54 1999
@@ -1,6 +1,6 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -241,7 +241,10 @@
       if (parse_res == -1)
 	return NSS_STATUS_TRYAGAIN;
       else
-	return NSS_STATUS_NOTFOUND;
+	{
+	  *errnop = ENOENT;
+	  return NSS_STATUS_NOTFOUND;
+	}
     }
   return NSS_STATUS_SUCCESS;
 }
diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/nss_nis/nis-grp.c glibc-2.2/nis/nss_nis/nis-grp.c
--- ../src/glibc-2.2/nis/nss_nis/nis-grp.c	Wed Aug 19 01:24:47 1998
+++ glibc-2.2/nis/nss_nis/nis-grp.c	Sun Dec 12 15:41:54 1999
@@ -1,6 +1,6 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -207,7 +207,7 @@
 	return NSS_STATUS_TRYAGAIN;
       else
 	{
-	  *errnop = EAGAIN;
+	  *errnop = ENOENT;
 	  return NSS_STATUS_NOTFOUND;
 	}
     }
diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/nss_nis/nis-hosts.c glibc-2.2/nis/nss_nis/nis-hosts.c
--- ../src/glibc-2.2/nis/nss_nis/nis-hosts.c	Mon Aug 30 21:13:24 1999
+++ glibc-2.2/nis/nss_nis/nis-hosts.c	Sun Dec 12 15:41:54 1999
@@ -321,6 +321,7 @@
       else
 	{
 	  *h_errnop = HOST_NOT_FOUND;
+	  *errnop = ENOENT;
 	  return NSS_STATUS_NOTFOUND;
 	}
     }
@@ -392,7 +393,10 @@
 	  *errnop = errno;
 	}
       if (retval == NSS_STATUS_NOTFOUND)
-	*h_errnop = HOST_NOT_FOUND;
+	{
+	  *h_errnop = HOST_NOT_FOUND;
+	  *errnop = ENOENT;
+	}
       return retval;
     }
 
diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/nss_nis/nis-netgrp.c glibc-2.2/nis/nss_nis/nis-netgrp.c
--- ../src/glibc-2.2/nis/nss_nis/nis-netgrp.c	Mon Dec  8 03:39:59 1997
+++ glibc-2.2/nis/nss_nis/nis-netgrp.c	Sun Dec 12 15:41:54 1999
@@ -1,6 +1,6 @@
-/* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -117,7 +117,10 @@
   enum nss_status status;
 
   if (cursor == NULL)
-    return NSS_STATUS_NOTFOUND;
+    {
+      *errnop = ENOENT;
+      return NSS_STATUS_NOTFOUND;
+    }
 
   __libc_lock_lock (lock);
 
diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/nss_nis/nis-publickey.c glibc-2.2/nis/nss_nis/nis-publickey.c
--- ../src/glibc-2.2/nis/nss_nis/nis-publickey.c	Wed Aug 19 01:24:52 1998
+++ glibc-2.2/nis/nss_nis/nis-publickey.c	Sun Dec 12 15:41:54 1999
@@ -1,6 +1,6 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -50,8 +50,11 @@
 
   domain = strchr (netname, '@');
   if (!domain)
-    return NSS_STATUS_UNAVAIL;
-  domain++;
+    {
+      *errnop = EINVAL;
+      return NSS_STATUS_UNAVAIL;
+    }
+  ++domain;
 
   retval = yperr2nss (yp_match (domain, "publickey.byname", netname,
 				strlen (netname), &result, &len));
@@ -95,8 +98,11 @@
 
   domain = strchr (netname, '@');
   if (!domain)
-    return NSS_STATUS_UNAVAIL;
-  domain++;
+    {
+      *errnop = EINVAL;
+      return NSS_STATUS_UNAVAIL;
+    }
+  ++domain;
 
   retval = yperr2nss (yp_match (domain, "publickey.byname", netname,
 				strlen (netname), &result, &len));
@@ -196,10 +202,13 @@
 
   domain = strchr (netname, '@');
   if (!domain)
-    return NSS_STATUS_UNAVAIL;
+    {
+      *errnop = EINVAL;
+      return NSS_STATUS_UNAVAIL;
+    }
 
   /* Point past the '@' character */
-  domain++;
+  ++domain;
   lookup = NULL;
   yperr = yp_match (domain, "netid.byname", netname, strlen (netname),
 		    &lookup, &len);
@@ -209,11 +218,13 @@
       break;			/* the successful case */
     case YPERR_DOMAIN:
     case YPERR_KEY:
+      *errnop = ENOENT;
       return NSS_STATUS_NOTFOUND;
     case YPERR_MAP:
     default:
       return NSS_STATUS_UNAVAIL;
     }
+
   if (lookup)
     {
       enum nss_status err;
@@ -224,7 +235,9 @@
       return err;
     }
   else
-    return NSS_STATUS_NOTFOUND;
-
+    {
+      *errnop = ENOENT;
+      return NSS_STATUS_NOTFOUND;
+    }
   return NSS_STATUS_SUCCESS;
 }
diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/nss_nis/nis-service.c glibc-2.2/nis/nss_nis/nis-service.c
--- ../src/glibc-2.2/nis/nss_nis/nis-service.c	Tue Aug  3 19:16:06 1999
+++ glibc-2.2/nis/nss_nis/nis-service.c	Sun Dec 12 15:41:54 1999
@@ -172,7 +172,7 @@
 	  return NSS_STATUS_NOTFOUND;
 	}
       p = strncpy (buffer, data->next->val, buflen);
-           while (isspace (*p))
+      while (isspace (*p))
         ++p;
 
       parse_res = _nss_files_parse_servent (p, serv, pdata, buflen, errnop);
@@ -215,6 +215,63 @@
       return NSS_STATUS_UNAVAIL;
     }
 
+  /* If the protocol is given, we could try if our NIS server knows
+     about services.byservicename map. If yes, we only need one query */
+  if (protocol != NULL)
+    {
+      char key[strlen (name) + strlen (protocol) + 2];
+      char *cp, *domain, *result;
+      size_t keylen, len;
+
+      /* If this fails, the other solution will also fail. */
+      if (yp_get_default_domain (&domain))
+	return NSS_STATUS_UNAVAIL;
+
+      /* key is: "name/protocol" */
+      cp = stpcpy (key, name);
+      *cp++ = '/';
+      stpcpy (cp, protocol);
+      keylen = strlen (key);
+      status = yperr2nss (yp_match (domain, "services.byservicename", key,
+				    keylen, &result, &len));
+
+      /* If we found the key, it's ok and parse the result. If not,
+	 fall through and parse the complete table. */
+      if (status == NSS_STATUS_SUCCESS)
+	{
+	  struct parser_data *pdata = (void *) buffer;
+	  int parse_res;
+	  char *p;
+
+	  if ((size_t) (len + 1) > buflen)
+	    {
+	      free (result);
+	      *errnop = ERANGE;
+	      return NSS_STATUS_TRYAGAIN;
+	    }
+
+	  p = strncpy (buffer, result, len);
+	  buffer[len] = '\0';
+	  while (isspace (*p))
+	    ++p;
+	  free (result);
+	  parse_res = _nss_files_parse_servent (p, serv, pdata,
+						buflen, errnop);
+	  if (parse_res < 0)
+	    {
+	      if (parse_res == -1)
+		return NSS_STATUS_TRYAGAIN;
+	      else
+		{
+		  *errnop = ENOENT;
+		  return NSS_STATUS_NOTFOUND;
+		}
+	    }
+	  else
+	    return NSS_STATUS_SUCCESS;
+	}
+    }
+
   status = internal_nis_setservent (&data);
   if (status != NSS_STATUS_SUCCESS)
     return status;
@@ -256,10 +313,57 @@
   enum nss_status status;
   int found;
 
-  if (protocol == NULL)
+  /* If the protocol is given, we only need one query */
+  if (protocol != NULL)
     {
-      *errnop = EINVAL;
-      return NSS_STATUS_UNAVAIL;
+      char key[100 + strlen (protocol) + 2];
+      char *domain, *result;
+      size_t keylen, len;
+
+      /* If this fails, the other solution will also fail. */
+      if (yp_get_default_domain (&domain))
+	return NSS_STATUS_UNAVAIL;
+
+      /* key is: "port/protocol" */
+      keylen = snprintf (key, sizeof (key), "%d/%s", port, protocol);
+      status = yperr2nss (yp_match (domain, "services.byname", key,
+				    keylen, &result, &len));
+
+      /* If we found the key, it's ok and parse the result. If not,
+	 fall through and parse the complete table. */
+      if (status == NSS_STATUS_SUCCESS)
+	{
+	  struct parser_data *pdata = (void *) buffer;
+	  int parse_res;
+	  char *p;
+
+	  if ((size_t) (len + 1) > buflen)
+	    {
+	      free (result);
+	      *errnop = ERANGE;
+	      return NSS_STATUS_TRYAGAIN;
+	    }
+
+	  p = strncpy (buffer, result, len);
+	  buffer[len] = '\0';
+	  while (isspace (*p))
+	    ++p;
+	  free (result);
+	  parse_res = _nss_files_parse_servent (p, serv, pdata,
+						buflen, errnop);
+	  if (parse_res < 0)
+	    {
+	      if (parse_res == -1)
+		return NSS_STATUS_TRYAGAIN;
+	      else
+		{
+		  *errnop = ENOENT;
+		  return NSS_STATUS_NOTFOUND;
+		}
+	    }
+	  else
+	    return NSS_STATUS_SUCCESS;
+	}
     }
 
   status = internal_nis_setservent (&data);
@@ -270,7 +374,8 @@
   while (!found &&
          ((status = internal_nis_getservent_r (serv, buffer, buflen, errnop,
 					       &data)) == NSS_STATUS_SUCCESS))
-    if (serv->s_port == port && strcmp (serv->s_proto, protocol) == 0)
+    if (serv->s_port == port &&
+	(protocol == NULL || strcmp (serv->s_proto, protocol) == 0))
       found = 1;
 
   internal_nis_endservent (&data);
diff -u -r --new-file --exclude=CVS ../src/glibc-2.2/nis/ypclnt.c glibc-2.2/nis/ypclnt.c
--- ../src/glibc-2.2/nis/ypclnt.c	Sat Jun 19 11:47:17 1999
+++ glibc-2.2/nis/ypclnt.c	Sun Dec 12 15:41:54 1999
@@ -1,6 +1,6 @@
 /* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
+   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
 
    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
@@ -42,7 +42,6 @@
     struct sockaddr_in dom_server_addr;
     int dom_socket;
     CLIENT *dom_client;
-    long int dom_vers;
   };
 typedef struct dom_binding dom_binding;
 
@@ -63,7 +62,6 @@
   int clnt_sock;
   CLIENT *client;
   int is_new = 0;
-  int try;
 
   if (domain == NULL || domain[0] == '\0')
     return YPERR_BADARGS;
@@ -83,137 +81,127 @@
     {
       is_new = 1;
       ysd = (dom_binding *) calloc (1, sizeof *ysd);
-      ysd->dom_socket = -1;
-      ysd->dom_vers = -1;
     }
 
-  try = 0;
-
-  do
-    {
-      ++try;
-      if (try > MAXTRIES)
-        {
-          if (is_new)
-            free (ysd);
-          return YPERR_YPBIND;
-        }
-
 #if USE_BINDINGDIR
-      if (ysd->dom_vers < 1 && try == 1) /* Try binding dir only first time */
+  if (ysd->dom_client == NULL)
+    {
+      /* Try binding dir at first if we have no binding */
+      char path[sizeof (BINDINGDIR) + strlen (domain) + 10];
+      struct iovec vec[2];
+      unsigned short port;
+      int fd;
+
+      sprintf (path, "%s/%s.%d", BINDINGDIR, domain, YPBINDVERS);
+      fd = open (path, O_RDONLY);
+      if (fd >= 0)
 	{
-	  char path[sizeof (BINDINGDIR) - 1 + strlen (domain) + 10];
-	  struct iovec vec[2];
-	  unsigned short port;
-	  int fd;
-
-	  sprintf (path, "%s/%s.%d", BINDINGDIR, domain, YPBINDVERS);
-	  fd = open (path, O_RDONLY);
-	  if (fd >= 0)
+	  /* We have a binding file and could save a RPC call */
+	  vec[0].iov_base = &port;
+	  vec[0].iov_len = sizeof (port);
+	  vec[1].iov_base = &ypbr;
+	  vec[1].iov_len = sizeof (ypbr);
+
+	  if (readv (fd, vec, 2) == sizeof (port) + sizeof (ypbr))
 	    {
-	      /* We have a binding file and could save a RPC call */
-	      vec[0].iov_base = &port;
-	      vec[0].iov_len = sizeof (port);
-	      vec[1].iov_base = &ypbr;
-	      vec[1].iov_len = sizeof (ypbr);
-
-	      if (readv (fd, vec, 2) == sizeof (port) + sizeof (ypbr))
-		{
-		  ysd->dom_server_addr.sin_family = AF_INET;
-		  memcpy (&ysd->dom_server_addr.sin_port,
-			  ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
-			  sizeof (ysd->dom_server_addr.sin_port));
-		  memcpy (&ysd->dom_server_addr.sin_addr.s_addr,
-			  ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
-			  sizeof (ysd->dom_server_addr.sin_addr.s_addr));
-		  ysd->dom_vers = YPVERS;
-		  strncpy (ysd->dom_domain, domain, YPMAXDOMAIN);
-		  ysd->dom_domain[YPMAXDOMAIN] = '\0';
-		}
-	      close (fd);
+	      ysd->dom_server_addr.sin_family = AF_INET;
+	      memcpy (&ysd->dom_server_addr.sin_port,
+		      ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
+		      sizeof (ysd->dom_server_addr.sin_port));
+	      memcpy (&ysd->dom_server_addr.sin_addr.s_addr,
+		      ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
+		      sizeof (ysd->dom_server_addr.sin_addr.s_addr));
+	      strncpy (ysd->dom_domain, domain, YPMAXDOMAIN);
+	      ysd->dom_domain[YPMAXDOMAIN] = '\0';
+
+	      ysd->dom_socket = RPC_ANYSOCK;
+	      ysd->dom_client = clntudp_create (&ysd->dom_server_addr, YPPROG,
+						YPVERS, UDPTIMEOUT,
+						&ysd->dom_socket);
+
+	      if (ysd->dom_client != NULL)
+		/* If the program exits, close the socket */
+		if (fcntl (ysd->dom_socket, F_SETFD, 1) == -1)
+		  perror ("fcntl: F_SETFD");
 	    }
+	  close (fd);
 	}
+    }
 #endif /* USE_BINDINGDIR */
 
-      if (ysd->dom_vers == -1)
+  if (ysd->dom_client == NULL)
+    {
+      memset (&clnt_saddr, '\0', sizeof clnt_saddr);
+      clnt_saddr.sin_family = AF_INET;
+      clnt_saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+      clnt_sock = RPC_ANYSOCK;
+      client = clnttcp_create (&clnt_saddr, YPBINDPROG, YPBINDVERS,
+			       &clnt_sock, 0, 0);
+      if (client == NULL)
 	{
-	  if (ysd->dom_client)
-	    {
-	      clnt_destroy (ysd->dom_client);
-	      ysd->dom_client = NULL;
-	      ysd->dom_socket = -1;
-	    }
-          memset (&clnt_saddr, '\0', sizeof clnt_saddr);
-          clnt_saddr.sin_family = AF_INET;
-          clnt_saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
-          clnt_sock = RPC_ANYSOCK;
-          client = clnttcp_create (&clnt_saddr, YPBINDPROG, YPBINDVERS,
-                                   &clnt_sock, 0, 0);
-          if (client == NULL)
-            {
-              if (is_new)
-                free (ysd);
-              return YPERR_YPBIND;
-            }
-          /*
-          ** Check the port number -- should be < IPPORT_RESERVED.
-          ** If not, it's possible someone has registered a bogus
-          ** ypbind with the portmapper and is trying to trick us.
-          */
-          if (ntohs (clnt_saddr.sin_port) >= IPPORT_RESERVED)
-            {
-              clnt_destroy (client);
-              if (is_new)
-                free (ysd);
-              return YPERR_YPBIND;
-            }
-
-          if (clnt_call (client, YPBINDPROC_DOMAIN,
-                         (xdrproc_t) xdr_domainname, (caddr_t) &domain,
-                         (xdrproc_t) xdr_ypbind_resp,
-                         (caddr_t) &ypbr, RPCTIMEOUT) != RPC_SUCCESS)
-            {
-              clnt_destroy (client);
-              if (is_new)
-                free (ysd);
-              return YPERR_YPBIND;
-            }
-
-          clnt_destroy (client);
-
-          if (ypbr.ypbind_status != YPBIND_SUCC_VAL)
-            {
-	      fprintf (stderr, _("YPBINDPROC_DOMAIN: %s\n"),
-		       ypbinderr_string (ypbr.ypbind_resp_u.ypbind_error));
-	      if (is_new)
-		free (ysd);
-	      return YPERR_DOMAIN;
-	    }
-          memset (&ysd->dom_server_addr, '\0', sizeof ysd->dom_server_addr);
-          ysd->dom_server_addr.sin_family = AF_INET;
-          memcpy (&ysd->dom_server_addr.sin_port,
-                  ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
-                  sizeof (ysd->dom_server_addr.sin_port));
-          memcpy (&ysd->dom_server_addr.sin_addr.s_addr,
-                  ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
-                  sizeof (ysd->dom_server_addr.sin_addr.s_addr));
-          ysd->dom_vers = YPVERS;
-          strncpy (ysd->dom_domain, domain, YPMAXDOMAIN);
-	  ysd->dom_domain[YPMAXDOMAIN] = '\0';
-        }
+	  if (is_new)
+	    free (ysd);
+	  return YPERR_YPBIND;
+	}
+      /* Check the port number -- should be < IPPORT_RESERVED.
+	 If not, it's possible someone has registered a bogus
+	 ypbind with the portmapper and is trying to trick us. */
+      if (ntohs (clnt_saddr.sin_port) >= IPPORT_RESERVED)
+	{
+	  clnt_destroy (client);
+	  if (is_new)
+	    free (ysd);
+	  return YPERR_YPBIND;
+	}
+
+      if (clnt_call (client, YPBINDPROC_DOMAIN,
+		     (xdrproc_t) xdr_domainname, (caddr_t) &domain,
+		     (xdrproc_t) xdr_ypbind_resp,
+		     (caddr_t) &ypbr, RPCTIMEOUT) != RPC_SUCCESS)
+	{
+	  clnt_destroy (client);
+	  if (is_new)
+	    free (ysd);
+	  return YPERR_YPBIND;
+	}
+
+      clnt_destroy (client);
+
+      if (ypbr.ypbind_status != YPBIND_SUCC_VAL)
+	{
+	  fprintf (stderr, _("YPBINDPROC_DOMAIN: %s\n"),
+		   ypbinderr_string (ypbr.ypbind_resp_u.ypbind_error));
+	  if (is_new)
+	    free (ysd);
+	  return YPERR_DOMAIN;
+	}
+      memset (&ysd->dom_server_addr, '\0', sizeof ysd->dom_server_addr);
+      ysd->dom_server_addr.sin_family = AF_INET;
+      memcpy (&ysd->dom_server_addr.sin_port,
+	      ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
+	      sizeof (ysd->dom_server_addr.sin_port));
+      memcpy (&ysd->dom_server_addr.sin_addr.s_addr,
+	      ypbr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
+	      sizeof (ysd->dom_server_addr.sin_addr.s_addr));
+      strncpy (ysd->dom_domain, domain, YPMAXDOMAIN);
+      ysd->dom_domain[YPMAXDOMAIN] = '\0';
 
       ysd->dom_socket = RPC_ANYSOCK;
       ysd->dom_client = clntudp_create (&ysd->dom_server_addr, YPPROG, YPVERS,
                                         UDPTIMEOUT, &ysd->dom_socket);
-      if (ysd->dom_client == NULL)
-        ysd->dom_vers = -1;
 
+      if (ysd->dom_client != NULL)
+	/* If the program exits, close the socket */
+	if (fcntl (ysd->dom_socket, F_SETFD, 1) == -1)
+	  perror ("fcntl: F_SETFD");
     }
-  while (ysd->dom_client == NULL);
 
-  /* If the program exists, close the socket */
-  if (fcntl (ysd->dom_socket, F_SETFD, 1) == -1)
-    perror ("fcntl: F_SETFD");
+  if (ysd->dom_client == NULL)
+    {
+      if (is_new)
+	free (ysd);
+      return YPERR_YPSERV;
+    }
 
   if (is_new && ypdb != NULL)
     {
@@ -229,7 +217,60 @@
 {
   clnt_destroy (ydb->dom_client);
   ydb->dom_client = NULL;
-  ydb->dom_socket = -1;
+}
+
+int
+yp_bind (const char *indomain)
+{
+  int status;
+
+  __libc_lock_lock (ypbindlist_lock);
+
+  status = __yp_bind (indomain, &__ypbindlist);
+
+  __libc_lock_unlock (ypbindlist_lock);
+
+  return status;
+}
+
+static void
+yp_unbind_locked (const char *indomain)
+{
+  dom_binding *ydbptr, *ydbptr2;
+
+  ydbptr2 = NULL;
+  ydbptr = __ypbindlist;
+
+  while (ydbptr != NULL)
+    {
+      if (strcmp (ydbptr->dom_domain, indomain) == 0)
+	{
+	  dom_binding *work;
+
+	  work = ydbptr;
+	  if (ydbptr2 == NULL)
+	    __ypbindlist = __ypbindlist->dom_pnext;
+	  else
+	    ydbptr2 = ydbptr->dom_pnext;
+	  __yp_unbind (work);
+	  free (work);
+	  break;
+	}
+      ydbptr2 = ydbptr;
+      ydbptr = ydbptr->dom_pnext;
+    }
+}
+
+void
+yp_unbind (const char *indomain)
+{
+  __libc_lock_lock (ypbindlist_lock);
+
+  yp_unbind_locked (indomain);
+
+  __libc_lock_unlock (ypbindlist_lock);
+
+  return;
 }
 
 static int
@@ -278,14 +319,26 @@
 
       if (result != RPC_SUCCESS)
 	{
-	  clnt_perror (ydb->dom_client, "do_ypcall: clnt_call");
-	  ydb->dom_vers = -1;
-	  if (!use_ypbindlist)
+	  /* Don't print the error message on the first try. It
+	     could be that we use cached data which is now invalid. */
+	  if (try != 0)
+	    clnt_perror (ydb->dom_client, "do_ypcall: clnt_call");
+
+	  if (use_ypbindlist)
+	    {
+	      /* We use ypbindlist, and the old cached data is
+		 invalid. unbind now and create a new binding */
+	      yp_unbind_locked (domain);
+	      __libc_lock_unlock (ypbindlist_lock);
+	      use_ypbindlist = FALSE;
+	    }
+	  else
 	    {
 	      __yp_unbind (ydb);
 	      free (ydb);
-	      ydb = NULL;
 	    }
+
+	  ydb = NULL;
 	  status = YPERR_RPC;
 	}
       else
@@ -311,52 +364,6 @@
   return status;
 }
 
-int
-yp_bind (const char *indomain)
-{
-  int status;
-
-  __libc_lock_lock (ypbindlist_lock);
-
-  status = __yp_bind (indomain, &__ypbindlist);
-
-  __libc_lock_unlock (ypbindlist_lock);
-
-  return status;
-}
-
-void
-yp_unbind (const char *indomain)
-{
-  dom_binding *ydbptr, *ydbptr2;
-
-  __libc_lock_lock (ypbindlist_lock);
-
-  ydbptr2 = NULL;
-  ydbptr = __ypbindlist;
-  while (ydbptr != NULL)
-    {
-      if (strcmp (ydbptr->dom_domain, indomain) == 0)
-	{
-	  dom_binding *work;
-
-	  work = ydbptr;
-	  if (ydbptr2 == NULL)
-	    __ypbindlist = __ypbindlist->dom_pnext;
-	  else
-	    ydbptr2 = ydbptr->dom_pnext;
-	  __yp_unbind (work);
-	  free (work);
-	  break;
-	}
-      ydbptr2 = ydbptr;
-      ydbptr = ydbptr->dom_pnext;
-    }
-
-  __libc_lock_unlock (ypbindlist_lock);
-
-  return;
-}
 
 __libc_lock_define_initialized (static, domainname_lock)
 
@@ -374,7 +381,7 @@
 	result = YPERR_NODOM;
       else if (strcmp (__ypdomainname, "(none)") == 0)
 	{
-	  /* If domainname is not set, some Systems will return "(none)" */
+	  /* If domainname is not set, some systems will return "(none)" */
 	  __ypdomainname[0] = '\0';
 	  result = YPERR_NODOM;
 	}
@@ -629,6 +636,10 @@
 	    int keylen = resp.ypresp_all_u.val.key.keydat_len;
 	    int vallen = resp.ypresp_all_u.val.val.valdat_len;
 
+	    /* We are not allowed to modify the key and val data.
+	       But we are allowed to add data behind the buffer,
+	       if we don't modify the length. So add an extra NUL
+	       character to avoid trouble with broken code. */
 	    *objp = YP_TRUE;
 	    memcpy (key, resp.ypresp_all_u.val.key.keydat_val, keylen);
 	    key[keylen] = '\0';
@@ -640,14 +651,13 @@
 	      return TRUE;
 	  }
 	  break;
-	case YP_NOMORE:
-	  *objp = YP_NOMORE;
-	  xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
-	  return TRUE;
-	  break;
 	default:
 	  *objp = resp.ypresp_all_u.val.stat;
 	  xdr_free ((xdrproc_t) xdr_ypresp_all, (char *) &resp);
+	  /* Sun says we don't need to make this call, but must return
+	     immediatly. Since Solaris makes this call, we will call
+	     the callback function, too. */
+	  (*ypall_foreach) (*objp, NULL, 0, NULL, 0, ypall_data);
 	  return TRUE;
 	}
     }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]