summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--nss/nss_files/files-hosts.c200
-rw-r--r--resolv/Versions2
3 files changed, 194 insertions, 13 deletions
diff --git a/ChangeLog b/ChangeLog
index be7ced0956..ad3c6d2552 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2000-07-23  Ulrich Drepper  <drepper@redhat.com>
+
+	* nss/nss_files/files-hosts.c: Implement multi handling.
+	* resolv/Versions: Export _res_hconf for GLIBC_2.2 from libc.
+
 2000-07-22  Ulrich Drepper  <drepper@redhat.com>
 
 	* nss/nss_files/files-parse.c: Don't pollute namespace by using
diff --git a/nss/nss_files/files-hosts.c b/nss/nss_files/files-hosts.c
index e73fee0b14..c96a39c38e 100644
--- a/nss/nss_files/files-hosts.c
+++ b/nss/nss_files/files-hosts.c
@@ -17,6 +17,7 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
+#include <assert.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
@@ -26,6 +27,7 @@
 
 /* Get implementation for some internal functions.  */
 #include "../resolv/mapv4v6addr.h"
+#include "../resolv/res_hconf.h"
 
 
 #define ENTNAME		hostent
@@ -74,25 +76,199 @@ LINE_PARSER
    STRING_FIELD (result->h_name, isspace, 1);
  })
 
+
+
+#define HOST_DB_LOOKUP(name, keysize, keypattern, break_if_match, proto...) \
+enum nss_status								      \
+_nss_files_get##name##_r (proto,					      \
+			  struct STRUCTURE *result, char *buffer,	      \
+			  size_t buflen, int *errnop H_ERRNO_PROTO)	      \
+{									      \
+  enum nss_status status;						      \
+									      \
+  __libc_lock_lock (lock);						      \
+									      \
+  /* Reset file pointer to beginning or open file.  */			      \
+  status = internal_setent (keep_stream);				      \
+									      \
+  if (status == NSS_STATUS_SUCCESS)					      \
+    {									      \
+      /* Tell getent function that we have repositioned the file pointer.  */ \
+      last_use = getby;							      \
+									      \
+      while ((status = internal_getent (result, buffer, buflen, errnop	      \
+					H_ERRNO_ARG EXTRA_ARGS_VALUE))	      \
+	     == NSS_STATUS_SUCCESS)					      \
+	{ break_if_match }						      \
+									      \
+      if (status == NSS_STATUS_SUCCESS					      \
+	  && _res_hconf.flags & HCONF_FLAG_MULTI)			      \
+	{								      \
+	  /* We have to get all host entries from the file.  */		      \
+	  const size_t tmp_buflen = MIN (buflen, 4096);			      \
+	  char tmp_buffer[tmp_buflen];					      \
+	  struct hostent tmp_result_buf;				      \
+	  int naddrs = 1;						      \
+	  int naliases = 0;						      \
+	  char *bufferend;						      \
+									      \
+	  while (result->h_aliases[naliases] != NULL)			      \
+	    ++naliases;							      \
+									      \
+	  bufferend = (char *) &result->h_aliases[naliases + 1];	      \
+									      \
+	  while ((status = internal_getent (&tmp_result_buf, tmp_buffer,      \
+					    tmp_buflen, errnop H_ERRNO_ARG    \
+					    EXTRA_ARGS_VALUE))		      \
+		 == NSS_STATUS_SUCCESS)					      \
+	    {								      \
+	      int matches = 1;						      \
+	      struct hostent *old_result = result;			      \
+	      result = &tmp_result_buf;					      \
+	      /* The following piece is a bit clumsy but we want to use the   \
+		 `break_if_match' value.  The optimizer should do its	      \
+		 job.  */						      \
+	      do							      \
+		{							      \
+		  break_if_match					      \
+		  result = old_result;					      \
+		}							      \
+	      while ((matches = 0));					      \
+									      \
+	      if (matches)						      \
+		{							      \
+		  /* We could be very clever and try to recycle a few bytes   \
+		     in the buffer instead of generating new arrays.  But     \
+		     we are not doing this here since it's more work than     \
+		     it's worth.  Simply let the user provide a bit bigger    \
+		     buffer.  */					      \
+		  char **new_h_addr_list;				      \
+		  char **new_h_aliases;					      \
+		  int newaliases = 0;					      \
+		  size_t newstrlen = 0;					      \
+		  int cnt;						      \
+									      \
+		  /* Count the new aliases and the length of the strings.  */ \
+		  while (tmp_result_buf.h_aliases[newaliases] != NULL)	      \
+		    {							      \
+		      char *cp = tmp_result_buf.h_aliases[newaliases];	      \
+		      ++newaliases;					      \
+		      newstrlen += strlen (cp) + 1;			      \
+		    }							      \
+		  /* If the real name is different add it also to the	      \
+		     aliases.  This means that there is a duplication	      \
+		     in the alias list but this is really the users	      \
+		     problem.  */					      \
+		  if (strcmp (old_result->h_name,			      \
+			      tmp_result_buf.h_name) != 0)		      \
+		    {							      \
+		      ++newaliases;					      \
+		      newstrlen += strlen (tmp_result_buf.h_name) + 1;	      \
+		    }							      \
+									      \
+		  /* Now we can check whether the buffer is large enough.  */ \
+		  if (bufferend + 16 + (naddrs + 2) * sizeof (char *)	      \
+		      + roundup (newstrlen, sizeof (char *))		      \
+		      + (naliases + newaliases + 1) * sizeof (char *)	      \
+		      >= buffer + buflen)				      \
+		    {							      \
+		      *errnop = ERANGE;					      \
+		      status = NSS_STATUS_TRYAGAIN;			      \
+		      break;						      \
+		    }							      \
+									      \
+		  new_h_addr_list =					      \
+		    (char **) (bufferend				      \
+			       + roundup (newstrlen, sizeof (char *))	      \
+			       + 16);					      \
+		  new_h_aliases =					      \
+		    (char **) ((char *) new_h_addr_list			      \
+			       + (naddrs + 2) * sizeof (char *));	      \
+									      \
+		  /* Copy the old data in the new arrays.  */		      \
+		  for (cnt = 0; cnt < naddrs; ++cnt)			      \
+		    new_h_addr_list[cnt] = old_result->h_addr_list[cnt];      \
+									      \
+		  for (cnt = 0; cnt < naliases; ++cnt)			      \
+		    new_h_aliases[cnt] = old_result->h_aliases[cnt];	      \
+									      \
+		  /* Store the new strings.  */				      \
+		  cnt = 0;						      \
+		  while (tmp_result_buf.h_aliases[cnt] != NULL)		      \
+		    {							      \
+		      new_h_aliases[naliases++] = bufferend;		      \
+		      bufferend = (__stpcpy (bufferend,			      \
+					     tmp_result_buf.h_aliases[cnt])   \
+				   + 1);				      \
+		    }							      \
+									      \
+		  if (cnt < newaliases)					      \
+		    {							      \
+		      new_h_aliases[naliases++] = bufferend;		      \
+		      bufferend = __stpcpy (bufferend,			      \
+					    tmp_result_buf.h_name) + 1;	      \
+		    }							      \
+									      \
+		  /* Final NULL pointer.  */				      \
+		  new_h_aliases[naliases] = NULL;			      \
+									      \
+		  /* Round up the buffer end address.  */		      \
+		  bufferend += (sizeof (char *)				      \
+				- ((bufferend - (char *) 0)		      \
+				   % sizeof (char *)));			      \
+									      \
+		  /* Now the new address.  */				      \
+		  new_h_addr_list[naddrs++] =				      \
+		    memcpy (bufferend, tmp_result_buf.h_addr,		      \
+			    tmp_result_buf.h_length);			      \
+									      \
+		  /* Also here a final NULL pointer.  */		      \
+		  new_h_addr_list[naddrs] = NULL;			      \
+									      \
+		  /* Store the new array pointers.  */			      \
+		  old_result->h_aliases = new_h_aliases;		      \
+		  old_result->h_addr_list = new_h_addr_list;		      \
+									      \
+		  /* Compute the new buffer end.  */			      \
+		  bufferend = (char *) &new_h_aliases[naliases + 1];	      \
+		  assert (bufferend <= buffer + buflen);		      \
+		}							      \
+	    }								      \
+									      \
+	  if (status != NSS_STATUS_TRYAGAIN)				      \
+	    status = NSS_STATUS_SUCCESS;				      \
+	}								      \
+									      \
+									      \
+      if (! keep_stream)						      \
+	internal_endent ();						      \
+    }									      \
+									      \
+  __libc_lock_unlock (lock);						      \
+									      \
+  return status;							      \
+}
+
+
 #define EXTRA_ARGS_VALUE \
   , ((_res.options & RES_USE_INET6) ? AF_INET6 : AF_INET),		      \
   ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
 #include "files-XXX.c"
+HOST_DB_LOOKUP (hostbyname, ,,
+		{
+		  LOOKUP_NAME_CASE (h_name, h_aliases)
+		}, const char *name)
 
-DB_LOOKUP (hostbyname, ,,
-	   {
-	     LOOKUP_NAME_CASE (h_name, h_aliases)
-	   }, const char *name)
 
 #undef EXTRA_ARGS_VALUE
 /* XXX Is using _res to determine whether we want to convert IPv4 addresses
    to IPv6 addresses really the right thing to do?  */
 #define EXTRA_ARGS_VALUE \
   , af, ((_res.options & RES_USE_INET6) ? AI_V4MAPPED : 0)
-DB_LOOKUP (hostbyname2, ,,
-	   {
-	     LOOKUP_NAME_CASE (h_name, h_aliases)
-	   }, const char *name, int af)
+HOST_DB_LOOKUP (hostbyname2, ,,
+		{
+		  LOOKUP_NAME_CASE (h_name, h_aliases)
+		}, const char *name, int af)
 
 DB_LOOKUP (hostbyaddr, ,,
 	   {
@@ -104,7 +280,7 @@ DB_LOOKUP (hostbyaddr, ,,
 #undef EXTRA_ARGS_VALUE
 #define EXTRA_ARGS_VALUE \
   , af, flags
-DB_LOOKUP (ipnodebyname, ,,
-	   {
-	     LOOKUP_NAME_CASE (h_name, h_aliases)
-	   }, const char *name, int af, int flags)
+HOST_DB_LOOKUP (ipnodebyname, ,,
+		{
+		  LOOKUP_NAME_CASE (h_name, h_aliases)
+		}, const char *name, int af, int flags)
diff --git a/resolv/Versions b/resolv/Versions
index 0f9255d08b..98685243e2 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -20,7 +20,7 @@ libc {
   }
   GLIBC_2.2 {
     # r*
-    __res_state; __res_init; __res_nclose; __res_ninit;
+    __res_state; __res_init; __res_nclose; __res_ninit; _res_hconf;
   }
 }