about summary refs log tree commit diff
path: root/resolv
diff options
context:
space:
mode:
Diffstat (limited to 'resolv')
-rw-r--r--resolv/Makefile20
-rw-r--r--resolv/Versions9
-rw-r--r--resolv/ga_test.c99
-rw-r--r--resolv/gai_cancel.c48
-rw-r--r--resolv/gai_error.c28
-rw-r--r--resolv/gai_misc.c423
-rw-r--r--resolv/gai_misc.h100
-rw-r--r--resolv/gai_notify.c100
-rw-r--r--resolv/gai_suspend.c147
-rw-r--r--resolv/getaddrinfo_a.c168
-rw-r--r--resolv/netdb.h72
11 files changed, 1199 insertions, 15 deletions
diff --git a/resolv/Makefile b/resolv/Makefile
index 64afbcb896..fd056d0718 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1994,95,96,97,98,99,2000 Free Software Foundation, Inc.
+# Copyright (C) 1994,95,96,97,98,99,2000,2001 Free Software Foundation, Inc.
 # This file is part of the GNU C Library.
 
 # The GNU C Library is free software; you can redistribute it and/or
@@ -26,16 +26,16 @@ headers	:= resolv.h \
 	   arpa/nameser.h arpa/nameser_compat.h \
 	   sys/bitypes.h
 distribute := ../conf/portability.h mapv4v6addr.h mapv4v6hostent.h \
-	      Banner res_hconf.h res_debug.h README
+	      Banner res_hconf.h res_debug.h README gai_misc.h ga_test.c
 
 routines := herror inet_addr inet_ntop inet_pton nsap_addr res_init \
-	    res_hconf res_libc
+	    res_hconf res_libc gai_sigqueue
 
 tests = tst-aton
 
 include ../Makeconfig
 
-extra-libs := libresolv libnss_dns
+extra-libs := libresolv libanl libnss_dns
 extra-libs-others = $(extra-libs)
 libresolv-routines := gethnamaddr res_comp res_debug	\
 		      res_data res_mkquery res_query res_send		\
@@ -43,6 +43,9 @@ libresolv-routines := gethnamaddr res_comp res_debug	\
 		      ns_parse ns_name ns_netint ns_ttl ns_print	\
 		      ns_samedomain
 
+libanl-routines := gai_cancel gai_error gai_misc gai_notify gai_suspend \
+		   getaddrinfo_a
+
 subdir-dirs = nss_dns
 vpath %.c nss_dns
 
@@ -51,6 +54,10 @@ ifneq ($(build-static-nss),yes)
 libnss_dns-inhibit-o	= $(filter-out .os,$(object-suffixes))
 endif
 
+ifeq (yes,$(build-shared))
+tests: $(objpfx)ga_test
+endif
+
 include ../Rules
 
 CPPFLAGS += -Dgethostbyname=res_gethostbyname \
@@ -69,3 +76,8 @@ $(objpfx)libresolv.so: $(common-objpfx)libc.so
 
 # The DNS NSS modules needs the resolver.
 $(objpfx)libnss_dns.so: $(objpfx)libresolv.so $(common-objpfx)libc.so
+
+# The asynchronous name lookup code needs the thread library.
+$(objpfx)libanl.so: $(common-objpfx)libc.so $(shared-thread-library)
+
+$(objpfx)ga_test: $(objpfx)libanl.so $(shared-thread-library)
diff --git a/resolv/Versions b/resolv/Versions
index 8131f1dd7d..b76b112a4b 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -22,6 +22,9 @@ libc {
     # r*
     __res_state; __res_init; __res_nclose; __res_ninit; _res_hconf;
   }
+  GLIBC_2.2.3 {
+    __gai_sigqueue;
+  }
 }
 
 libresolv {
@@ -66,3 +69,9 @@ libnss_dns {
     _nss_dns_getnetbyname_r;
   }
 }
+
+libanl {
+  GLIBC_2.2.3 {
+    getaddrinfo_a; gai_cancel; gai_error; gai_suspend;
+  }
+}
diff --git a/resolv/ga_test.c b/resolv/ga_test.c
new file mode 100644
index 0000000000..673162f015
--- /dev/null
+++ b/resolv/ga_test.c
@@ -0,0 +1,99 @@
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int
+main (void)
+{
+#define N 10
+  struct gaicb reqmem[N];
+  struct gaicb *req[N];
+  int n;
+
+  for (n = 0; n < N; ++n)
+    {
+      asprintf (&reqmem[n].ar_name, "test%d.test.redhat.com", 140 + n);
+      reqmem[n].ar_service = NULL;
+      reqmem[n].ar_request = NULL;
+      reqmem[n].ar_result = NULL;
+      req[n] = &reqmem[n];
+    }
+
+  if (getaddrinfo_a (GAI_NOWAIT, req, N, NULL) != 0)
+    {
+      puts ("queue call failed");
+      exit (1);
+    }
+  else
+    puts ("queue call successful");
+
+  while (1)
+    {
+      int any = 0;
+
+      for (n = 0; n < N; ++n)
+	if (req[n] != NULL && gai_error (req[n]) != EAI_INPROGRESS)
+	  {
+	    if (gai_error (req[n]) == 0)
+	      {
+		struct addrinfo *runp = req[n]->ar_result;
+
+		while (runp != NULL)
+		  {
+		    switch (runp->ai_family)
+		      {
+		      case PF_INET:
+			{
+			  struct sockaddr_in *sinp;
+
+			  sinp = (struct sockaddr_in *) runp->ai_addr;
+			  printf ("%2d: %s = %s\n", n,
+				  req[n]->ar_name, inet_ntoa (sinp->sin_addr));
+			}
+			break;
+		      default:
+			printf ("%2d: family %d\n", n, runp->ai_family);
+			break;
+		      }
+		    runp = runp->ai_next;
+		  }
+	      }
+	    else
+	      printf ("error for %d: %s\n", n,
+		      gai_strerror (gai_error (req[n])));
+	    req[n] = NULL;
+	    break;
+	  }
+	else if (req[n] != NULL)
+	  any = 1;
+
+      if (n == N)
+	{
+	  if (any)
+	    gai_suspend (req, N, NULL);
+	  else
+	    break;
+	}
+    }
+
+  __libc_write(1,"got all\n", 8);
+
+  for (n = 0; n < N; ++n)
+    if (gai_error (&reqmem[n]) == 0)
+      {
+	struct addrinfo *runp = reqmem[n].ar_result;
+
+	while (runp != NULL)
+	  {
+	    struct addrinfo *oldp = runp;
+	    runp = runp->ai_next;
+	    freeaddrinfo (oldp);
+	  }
+      }
+
+  return 0;
+}
diff --git a/resolv/gai_cancel.c b/resolv/gai_cancel.c
new file mode 100644
index 0000000000..13c64886a4
--- /dev/null
+++ b/resolv/gai_cancel.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+   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 <netdb.h>
+#include <pthread.h>
+
+#include "gai_misc.h"
+
+
+int
+gai_cancel (struct gaicb *gaicbp)
+{
+  int result = 0;
+  int status;
+
+  /* Request the mutex.  */
+  pthread_mutex_lock (&__gai_requests_mutex);
+
+  /* Find the request among those queued but not yet running.  */
+  status = __gai_remove_request (gaicbp);
+  if (status == 0)
+    result = EAI_CANCELED;
+  else if (status > 0)
+    result = EAI_NOTCANCELED;
+  else
+    result = EAI_ALLDONE;
+
+  /* Release the mutex.  */
+  pthread_mutex_unlock (&__gai_requests_mutex);
+
+  return result;
+}
diff --git a/resolv/gai_error.c b/resolv/gai_error.c
new file mode 100644
index 0000000000..f3d36c5d34
--- /dev/null
+++ b/resolv/gai_error.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+   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 <netdb.h>
+
+#include "gai_misc.h"
+
+int
+gai_error (struct gaicb *req)
+{
+  return req->__return;
+}
diff --git a/resolv/gai_misc.c b/resolv/gai_misc.c
new file mode 100644
index 0000000000..b69a8f6578
--- /dev/null
+++ b/resolv/gai_misc.c
@@ -0,0 +1,423 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+   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 <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include "gai_misc.h"
+
+
+
+/* Pool of request list entries.  */
+static struct requestlist **pool;
+
+/* Number of total and allocated pool entries.  */
+static size_t pool_max_size;
+static size_t pool_size;
+
+/* We implement a two dimensional array but allocate each row separately.
+   The macro below determines how many entries should be used per row.
+   It should better be a power of two.  */
+#define ENTRIES_PER_ROW	32
+
+/* How many rows we allocate at once.  */
+#define ROWS_STEP	8
+
+/* List of available entries.  */
+static struct requestlist *freelist;
+
+/* Structure list of all currently processed requests.  */
+static struct requestlist *requests;
+static struct requestlist *requests_tail;
+
+/* Number of threads currently running.  */
+static int nthreads;
+
+/* Number of threads waiting for work to arrive. */
+static int idle_thread_count;
+
+
+/* These are the values used for optimization.  We will probably
+   create a funcion to set these values.  */
+static struct gaiinit optim =
+{
+  20,	/* int gai_threads;	Maximal number of threads.  */
+  64,	/* int gai_num;		Number of expected simultanious requests. */
+  0,
+  0,
+  0,
+  0,
+  1,
+  0
+};
+
+
+/* Since the list is global we need a mutex protecting it.  */
+pthread_mutex_t __gai_requests_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
+/* When you add a request to the list and there are idle threads present,
+   you signal this condition variable. When a thread finishes work, it waits
+   on this condition variable for a time before it actually exits. */
+pthread_cond_t __gai_new_request_notification = PTHREAD_COND_INITIALIZER;
+
+
+/* Functions to handle request list pool.  */
+static struct requestlist *
+get_elem (void)
+{
+  struct requestlist *result;
+
+  if (freelist == NULL)
+    {
+      struct requestlist *new_row;
+      int cnt;
+
+      if (pool_size + 1 >= pool_max_size)
+	{
+	  size_t new_max_size = pool_max_size + ROWS_STEP;
+	  struct requestlist **new_tab;
+
+	  new_tab = (struct requestlist **)
+	    realloc (pool, new_max_size * sizeof (struct requestlist *));
+
+	  if (new_tab == NULL)
+	    return NULL;
+
+	  pool_max_size = new_max_size;
+	  pool = new_tab;
+	}
+
+      /* Allocate the new row.  */
+      cnt = pool_size == 0 ? optim.gai_num : ENTRIES_PER_ROW;
+      new_row = (struct requestlist *) calloc (cnt,
+					       sizeof (struct requestlist));
+      if (new_row == NULL)
+	return NULL;
+
+      pool[pool_size++] = new_row;
+
+      /* Put all the new entries in the freelist.  */
+      do
+	{
+	  new_row->next = freelist;
+	  freelist = new_row++;
+	}
+      while (--cnt > 0);
+    }
+
+  result = freelist;
+  freelist = freelist->next;
+
+  return result;
+}
+
+
+struct requestlist *
+internal_function
+__gai_find_request (const struct gaicb *gaicbp)
+{
+  struct requestlist *runp;
+
+  runp = requests;
+  while (runp != NULL)
+    if (runp->gaicbp == gaicbp)
+      return runp;
+    else
+      runp = runp->next;
+
+  return NULL;
+}
+
+
+int
+internal_function
+__gai_remove_request (struct gaicb *gaicbp)
+{
+  struct requestlist *runp;
+  struct requestlist *lastp;
+
+  runp = requests;
+  lastp = NULL;
+  while (runp != NULL)
+    if (runp->gaicbp == gaicbp)
+      break;
+    else
+      {
+	lastp = runp;
+	runp = runp->next;
+      }
+
+  if (runp == NULL)
+    /* Not known.  */
+    return -1;
+  if (runp->running != 0)
+    /* Currently handled.  */
+    return 1;
+
+  /* Dequeue the request.  */
+  if (lastp == NULL)
+    requests = runp->next;
+  else
+    lastp->next = runp->next;
+  if (runp == requests_tail)
+    requests_tail = lastp;
+
+  return 0;
+}
+
+
+/* The thread handler.  */
+static void *handle_requests (void *arg);
+
+
+/* The main function of the async I/O handling.  It enqueues requests
+   and if necessary starts and handles threads.  */
+struct requestlist *
+internal_function
+__gai_enqueue_request (struct gaicb *gaicbp)
+{
+  struct requestlist *newp;
+  struct requestlist *lastp;
+
+  /* Get the mutex.  */
+  pthread_mutex_lock (&__gai_requests_mutex);
+
+  /* Get a new element for the waiting list.  */
+  newp = get_elem ();
+  if (newp == NULL)
+    {
+      pthread_mutex_unlock (&__gai_requests_mutex);
+      __set_errno (EAGAIN);
+      return NULL;
+    }
+  newp->running = 0;
+  newp->gaicbp = gaicbp;
+  newp->waiting = NULL;
+  newp->next = NULL;
+
+  lastp = requests_tail;
+  if (requests_tail == NULL)
+    requests = requests_tail = newp;
+  else
+    {
+      requests_tail->next = newp;
+      requests_tail = newp;
+    }
+
+  gaicbp->__return = EAI_INPROGRESS;
+
+  /* See if we need to and are able to create a thread.  */
+  if (nthreads < optim.gai_threads && idle_thread_count == 0)
+    {
+      pthread_t thid;
+      pthread_attr_t attr;
+
+      newp->running = 1;
+
+      /* Make sure the thread is created detached.  */
+      pthread_attr_init (&attr);
+      pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+
+      /* Now try to start a thread.  */
+      if (pthread_create (&thid, &attr, handle_requests, newp) == 0)
+	/* We managed to enqueue the request.  All errors which can
+	   happen now can be recognized by calls to `gai_error'.  */
+	++nthreads;
+      else
+	{
+	  if (nthreads == 0)
+	    {
+	      /* We cannot create a thread in the moment and there is
+		 also no thread running.  This is a problem.  `errno' is
+		 set to EAGAIN if this is only a temporary problem.  */
+	      assert (lastp->next == newp);
+	      lastp->next = NULL;
+	      requests_tail = lastp;
+
+	      newp->next = freelist;
+	      freelist = newp;
+
+	      newp = NULL;
+	    }
+	  else
+	    /* We are not handling the request after all.  */
+	    newp->running = 0;
+	}
+    }
+
+  /* Enqueue the request in the request queue.  */
+  if (newp != NULL)
+    {
+      /* If there is a thread waiting for work, then let it know that we
+	 have just given it something to do. */
+      if (idle_thread_count > 0)
+	pthread_cond_signal (&__gai_new_request_notification);
+    }
+
+  /* Release the mutex.  */
+  pthread_mutex_unlock (&__gai_requests_mutex);
+
+  return newp;
+}
+
+
+static void *
+handle_requests (void *arg)
+{
+  struct requestlist *runp = (struct requestlist *) arg;
+
+  do
+    {
+      /* If runp is NULL, then we were created to service the work queue
+	 in general, not to handle any particular request. In that case we
+	 skip the "do work" stuff on the first pass, and go directly to the
+	 "get work off the work queue" part of this loop, which is near the
+	 end. */
+      if (runp == NULL)
+	pthread_mutex_lock (&__gai_requests_mutex);
+      else
+	{
+	  /* Make the request.  */
+	  struct gaicb *req = runp->gaicbp;
+	  struct requestlist *srchp;
+	  struct requestlist *lastp;
+
+	  req->__return = getaddrinfo (req->ar_name, req->ar_service,
+				       req->ar_request, &req->ar_result);
+
+	  /* Get the mutex.  */
+	  pthread_mutex_lock (&__gai_requests_mutex);
+
+	  /* Send the signal to notify about finished processing of the
+	     request.  */
+	  __gai_notify (runp);
+
+	  /* Now dequeue the current request.  */
+	  lastp = NULL;
+	  srchp = requests;
+	  while (srchp != runp)
+	    {
+	      lastp = srchp;
+	      srchp = srchp->next;
+	    }
+	  assert (runp->running == 1);
+
+	  if (requests_tail == runp)
+	    requests_tail = lastp;
+	  if (lastp == NULL)
+	    requests = requests->next;
+	  else
+	    lastp->next = runp->next;
+
+	  /* Free the old element.  */
+	  runp->next = freelist;
+	  freelist = runp;
+	}
+
+      runp = requests;
+      while (runp != NULL && runp->running != 0)
+	runp = runp->next;
+
+      /* If the runlist is empty, then we sleep for a while, waiting for
+	 something to arrive in it. */
+      if (runp == NULL && optim.gai_idle_time >= 0)
+	{
+	  struct timeval now;
+	  struct timespec wakeup_time;
+
+	  ++idle_thread_count;
+	  gettimeofday (&now, NULL);
+	  wakeup_time.tv_sec = now.tv_sec + optim.gai_idle_time;
+	  wakeup_time.tv_nsec = now.tv_usec * 1000;
+	  if (wakeup_time.tv_nsec > 1000000000)
+	    {
+	      wakeup_time.tv_nsec -= 1000000000;
+	      ++wakeup_time.tv_sec;
+	    }
+	  pthread_cond_timedwait (&__gai_new_request_notification,
+				  &__gai_requests_mutex, &wakeup_time);
+	  --idle_thread_count;
+	  runp = requests;
+	  while (runp != NULL && runp->running != 0)
+	    runp = runp->next;
+	}
+
+      if (runp == NULL)
+	--nthreads;
+      else
+	{
+	  /* Mark the request as being worked on.  */
+	  assert (runp->running == 0);
+	  runp->running = 1;
+
+	  /* If we have a request to process, and there's still another in
+	     the run list, then we need to either wake up or create a new
+	     thread to service the request that is still in the run list. */
+	  if (requests != NULL)
+	    {
+	      /* There are at least two items in the work queue to work on.
+		 If there are other idle threads, then we should wake them
+		 up for these other work elements; otherwise, we should try
+		 to create a new thread. */
+	      if (idle_thread_count > 0)
+		pthread_cond_signal (&__gai_new_request_notification);
+	      else if (nthreads < optim.gai_threads)
+		{
+		  pthread_t thid;
+		  pthread_attr_t attr;
+
+		  /* Make sure the thread is created detached.  */
+		  pthread_attr_init (&attr);
+		  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+
+		  /* Now try to start a thread. If we fail, no big deal,
+		     because we know that there is at least one thread (us)
+		     that is working on lookup operations. */
+		  if (pthread_create (&thid, &attr, handle_requests, NULL)
+		      == 0)
+		    ++nthreads;
+		}
+	    }
+	}
+
+      /* Release the mutex.  */
+      pthread_mutex_unlock (&__gai_requests_mutex);
+    }
+  while (runp != NULL);
+
+  pthread_exit (NULL);
+}
+
+
+/* Free allocated resources.  */
+static void
+__attribute__ ((unused))
+free_res (void)
+{
+  size_t row;
+
+  for (row = 0; row < pool_max_size; ++row)
+    free (pool[row]);
+
+  free (pool);
+}
+text_set_element (__libc_subfreeres, free_res);
diff --git a/resolv/gai_misc.h b/resolv/gai_misc.h
new file mode 100644
index 0000000000..a37264e80b
--- /dev/null
+++ b/resolv/gai_misc.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+   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.  */
+
+#ifndef _GAI_MISC_H
+#define _GAI_MISC_H	1
+
+#include <netdb.h>
+#include <signal.h>
+
+
+/* Used to synchronize.  */
+struct waitlist
+  {
+    struct waitlist *next;
+
+    pthread_cond_t *cond;
+    volatile int *counterp;
+    /* The next field is used in asynchronous `lio_listio' operations.  */
+    struct sigevent *sigevp;
+    /* XXX See requestlist, it's used to work around the broken signal
+       handling in Linux.  */
+    pid_t caller_pid;
+  };
+
+
+/* Used to queue requests..  */
+struct requestlist
+  {
+    int running;
+
+    struct requestlist *next;
+
+    /* Pointer to the actual data.  */
+    struct gaicb *gaicbp;
+
+    /* List of waiting processes.  */
+    struct waitlist *waiting;
+  };
+
+/* To customize the implementation one can use the following struct.
+   This implementation follows the one in Irix.  */
+struct gaiinit
+  {
+    int gai_threads;		/* Maximal number of threads.  */
+    int gai_num;		/* Number of expected simultanious requests. */
+    int gai_locks;		/* Not used.  */
+    int gai_usedba;		/* Not used.  */
+    int gai_debug;		/* Not used.  */
+    int gai_numusers;		/* Not used.  */
+    int gai_idle_time;		/* Number of seconds before idle thread
+				   terminates.  */
+    int gai_reserved;
+  };
+
+
+/* Lock for global I/O list of requests.  */
+extern pthread_mutex_t __gai_requests_mutex;
+
+
+/* Enqueue request.  */
+extern struct requestlist *__gai_enqueue_request (struct gaicb *gaicbp)
+     internal_function;
+
+/* Find request on wait list.  */
+extern struct requestlist *__gai_find_request (const struct gaicb *gaicbp)
+     internal_function;
+
+/* Remove request from waitlist.  */
+extern int __gai_remove_request (struct gaicb *gaicbp)
+     internal_function;
+
+/* Notify initiator of request and tell this everybody listening.  */
+extern void __gai_notify (struct requestlist *req)
+     internal_function;
+
+/* Notify initiator of request.  */
+extern int __gai_notify_only (struct sigevent *sigev, pid_t caller_pid)
+     internal_function;
+
+/* Send the signal.  */
+extern int __gai_sigqueue (int sig, const union sigval val, pid_t caller_pid)
+     internal_function;
+
+#endif /* gai_misc.h */
diff --git a/resolv/gai_notify.c b/resolv/gai_notify.c
new file mode 100644
index 0000000000..b3e623a2af
--- /dev/null
+++ b/resolv/gai_notify.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+   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 <netdb.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include "gai_misc.h"
+
+
+static void *
+notify_func_wrapper (void *arg)
+{
+  struct sigevent *sigev = arg;
+  sigev->sigev_notify_function (sigev->sigev_value);
+  return NULL;
+}
+
+
+int
+internal_function
+__gai_notify_only (struct sigevent *sigev, pid_t caller_pid)
+{
+  int result = 0;
+
+  /* Send the signal to notify about finished processing of the request.  */
+  if (sigev->sigev_notify == SIGEV_THREAD)
+    {
+      /* We have to start a thread.  */
+      pthread_t tid;
+      pthread_attr_t attr, *pattr;
+
+      pattr = (pthread_attr_t *) sigev->sigev_notify_attributes;
+      if (pattr == NULL)
+	{
+	  pthread_attr_init (&attr);
+	  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+	  pattr = &attr;
+	}
+
+      if (pthread_create (&tid, pattr, notify_func_wrapper, sigev) < 0)
+	result = -1;
+    }
+  else if (sigev->sigev_notify == SIGEV_SIGNAL)
+    /* We have to send a signal.  */
+    if (__gai_sigqueue (sigev->sigev_signo, sigev->sigev_value, caller_pid)
+	< 0)
+      result = -1;
+
+  return result;
+}
+
+
+void
+internal_function
+__gai_notify (struct requestlist *req)
+{
+  struct waitlist *waitlist;
+
+  /* Now also notify possibly waiting threads.  */
+  waitlist = req->waiting;
+  while (waitlist != NULL)
+    {
+      struct waitlist *next = waitlist->next;
+
+      /* Decrement the counter.  This is used in both cases.  */
+      --*waitlist->counterp;
+
+      if (waitlist->sigevp == NULL)
+	pthread_cond_signal (waitlist->cond);
+      else
+	/* This is part of a asynchronous `getaddrinfo_a' operation.  If
+	   this request is the last one, send the signal.  */
+	if (*waitlist->counterp == 0)
+	  {
+	    __gai_notify_only (waitlist->sigevp, waitlist->caller_pid);
+	    /* This is tricky.  See getaddrinfo_a.c for the reason why
+	       this works.  */
+	    free ((void *) waitlist->counterp);
+	  }
+
+      waitlist = next;
+    }
+}
diff --git a/resolv/gai_suspend.c b/resolv/gai_suspend.c
new file mode 100644
index 0000000000..b82acb465f
--- /dev/null
+++ b/resolv/gai_suspend.c
@@ -0,0 +1,147 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+   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 <errno.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include "gai_misc.h"
+
+
+int
+gai_suspend (const struct gaicb *const list[], int ent,
+	     const struct timespec *timeout)
+{
+  struct waitlist waitlist[ent];
+  struct requestlist *requestlist[ent];
+  pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+  int cnt;
+  int dummy;
+  int none = 1;
+  int result;
+
+  /* Request the mutex.  */
+  pthread_mutex_lock (&__gai_requests_mutex);
+
+  /* There is not yet a finished request.  Signal the request that
+     we are working for it.  */
+  for (cnt = 0; cnt < ent; ++cnt)
+    if (list[cnt] != NULL && list[cnt]->__return == EAI_INPROGRESS)
+      {
+	requestlist[cnt] = __gai_find_request (list[cnt]);
+
+	if (requestlist[cnt] != NULL)
+	  {
+	    waitlist[cnt].cond = &cond;
+	    waitlist[cnt].next = requestlist[cnt]->waiting;
+	    waitlist[cnt].counterp = &dummy;
+	    waitlist[cnt].sigevp = NULL;
+	    waitlist[cnt].caller_pid = 0;	/* Not needed.  */
+	    requestlist[cnt]->waiting = &waitlist[cnt];
+	    none = 0;
+	  }
+      }
+
+  if (none)
+    {
+      if (cnt < ent)
+	/* There is an entry which is finished.  */
+	result = 0;
+      else
+	result = EAI_ALLDONE;
+    }
+  else
+    {
+      /* There is no request done but some are still being worked on.  */
+      int oldstate;
+
+      /* Since `pthread_cond_wait'/`pthread_cond_timedwait' are cancelation
+	 points we must be careful.  We added entries to the waiting lists
+	 which we must remove.  So defer cancelation for now.  */
+      pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
+
+      if (timeout == NULL)
+	result = pthread_cond_wait (&cond, &__gai_requests_mutex);
+      else
+	{
+	  /* We have to convert the relative timeout value into an
+	     absolute time value with pthread_cond_timedwait expects.  */
+	  struct timeval now;
+	  struct timespec abstime;
+
+	  __gettimeofday (&now, NULL);
+	  abstime.tv_nsec = timeout->tv_nsec + now.tv_usec * 1000;
+	  abstime.tv_sec = timeout->tv_sec + now.tv_sec;
+	  if (abstime.tv_nsec >= 1000000000)
+	    {
+	      abstime.tv_nsec -= 1000000000;
+	      abstime.tv_sec += 1;
+	    }
+
+	  result = pthread_cond_timedwait (&cond, &__gai_requests_mutex,
+					   &abstime);
+	}
+
+      /* Now remove the entry in the waiting list for all requests
+	 which didn't terminate.  */
+      for (cnt = 0; cnt < ent; ++cnt)
+	if (list[cnt] != NULL && list[cnt]->__return == EAI_INPROGRESS
+	    && requestlist[cnt] != NULL)
+	  {
+	    struct waitlist **listp = &requestlist[cnt]->waiting;
+
+	    /* There is the chance that we cannot find our entry anymore.
+	       This could happen if the request terminated and restarted
+	       again.  */
+	    while (*listp != NULL && *listp != &waitlist[cnt])
+	      listp = &(*listp)->next;
+
+	    if (*listp != NULL)
+	      *listp = (*listp)->next;
+	  }
+
+      /* Now it's time to restore the cancelation state.  */
+      pthread_setcancelstate (oldstate, NULL);
+
+      /* Release the conditional variable.  */
+      if (pthread_cond_destroy (&cond) != 0)
+	/* This must never happen.  */
+	abort ();
+
+      if (result != 0)
+	{
+	  /* An error occurred.  Possibly it's EINTR.  We have to translate
+	     the timeout error report of `pthread_cond_timedwait' to the
+	     form expected from `gai_suspend'.  */
+	  if (__builtin_expect (result, ETIMEDOUT) == ETIMEDOUT)
+	    result = EAI_AGAIN;
+	  else if (result == EINTR)
+	    result = EAI_INTR;
+	  else
+	    result = EAI_SYSTEM;
+	}
+    }
+
+  /* Release the mutex.  */
+  pthread_mutex_unlock (&__gai_requests_mutex);
+
+  return result;
+}
diff --git a/resolv/getaddrinfo_a.c b/resolv/getaddrinfo_a.c
new file mode 100644
index 0000000000..1a077d1bc7
--- /dev/null
+++ b/resolv/getaddrinfo_a.c
@@ -0,0 +1,168 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+   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 <errno.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "gai_misc.h"
+
+
+/* We need this special structure to handle asynchronous I/O.  */
+struct async_waitlist
+  {
+    int counter;
+    struct sigevent sigev;
+    struct waitlist list[0];
+  };
+
+
+int
+getaddrinfo_a (int mode, struct gaicb *list[], int ent, struct sigevent *sig)
+{
+  struct sigevent defsigev;
+  struct requestlist *requests[ent];
+  int cnt;
+  volatile int total = 0;
+  int result = 0;
+
+  /* Check arguments.  */
+  if (mode != GAI_WAIT && mode != GAI_NOWAIT)
+    {
+      __set_errno (EINVAL);
+      return EAI_SYSTEM;
+    }
+
+  if (sig == NULL)
+    {
+      defsigev.sigev_notify = SIGEV_NONE;
+      sig = &defsigev;
+    }
+
+  /* Request the mutex.  */
+  pthread_mutex_lock (&__gai_requests_mutex);
+
+  /* Now we can enqueue all requests.  Since we already acquired the
+     mutex the enqueue function need not do this.  */
+  for (cnt = 0; cnt < ent; ++cnt)
+    if (list[cnt] != NULL)
+      {
+	requests[cnt] = __gai_enqueue_request (list[cnt]);
+
+	if (requests[cnt] != NULL)
+	  /* Successfully enqueued.  */
+	  ++total;
+	else
+	  /* Signal that we've seen an error.  `errno' and the error code
+	     of the gaicb will tell more.  */
+	  result = EAI_SYSTEM;
+      }
+    else
+      requests[cnt] = NULL;
+
+  if (total == 0)
+    {
+      /* We don't have anything to do except signalling if we work
+	 asynchronously.  */
+
+      /* Release the mutex.  We do this before raising a signal since the
+	 signal handler might do a `siglongjmp' and then the mutex is
+	 locked forever.  */
+      pthread_mutex_unlock (&__gai_requests_mutex);
+
+      if (mode == GAI_NOWAIT)
+	__gai_notify_only (sig,
+			   sig->sigev_notify == SIGEV_SIGNAL ? getpid () : 0);
+
+      return result;
+    }
+  else if (mode == GAI_WAIT)
+    {
+      pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+      struct waitlist waitlist[ent];
+      int oldstate;
+
+      total = 0;
+      for (cnt = 0; cnt < ent; ++cnt)
+	if (requests[cnt] != NULL)
+	  {
+	    waitlist[cnt].cond = &cond;
+	    waitlist[cnt].next = requests[cnt]->waiting;
+	    waitlist[cnt].counterp = &total;
+	    waitlist[cnt].sigevp = NULL;
+	    waitlist[cnt].caller_pid = 0;	/* Not needed.  */
+	    requests[cnt]->waiting = &waitlist[cnt];
+	    ++total;
+	  }
+
+      /* Since `pthread_cond_wait'/`pthread_cond_timedwait' are cancelation
+	 points we must be careful.  We added entries to the waiting lists
+	 which we must remove.  So defer cancelation for now.  */
+      pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
+
+      while (total > 0)
+	pthread_cond_wait (&cond, &__gai_requests_mutex);
+
+      /* Now it's time to restore the cancelation state.  */
+      pthread_setcancelstate (oldstate, NULL);
+
+      /* Release the conditional variable.  */
+      if (pthread_cond_destroy (&cond) != 0)
+	/* This must never happen.  */
+	abort ();
+    }
+  else
+    {
+      struct async_waitlist *waitlist;
+
+      waitlist = (struct async_waitlist *)
+	malloc (sizeof (struct async_waitlist)
+		+ (ent * sizeof (struct waitlist)));
+
+      if (waitlist == NULL)
+	result = EAI_AGAIN;
+      else
+	{
+	  pid_t caller_pid = sig->sigev_notify == SIGEV_SIGNAL ? getpid () : 0;
+	  total = 0;
+
+	  for (cnt = 0; cnt < ent; ++cnt)
+	    if (requests[cnt] != NULL)
+	      {
+		waitlist->list[cnt].cond = NULL;
+		waitlist->list[cnt].next = requests[cnt]->waiting;
+		waitlist->list[cnt].counterp = &waitlist->counter;
+		waitlist->list[cnt].sigevp = &waitlist->sigev;
+		waitlist->list[cnt].caller_pid = caller_pid;
+		requests[cnt]->waiting = &waitlist->list[cnt];
+		++total;
+	      }
+
+	  waitlist->counter = total;
+	  waitlist->sigev = *sig;
+	}
+    }
+
+  /* Release the mutex.  */
+  pthread_mutex_unlock (&__gai_requests_mutex);
+
+  return result;
+}
diff --git a/resolv/netdb.h b/resolv/netdb.h
index ae3f95aca1..79c147e6bb 100644
--- a/resolv/netdb.h
+++ b/resolv/netdb.h
@@ -33,6 +33,11 @@
 # include <rpc/netdb.h>
 #endif
 
+#ifdef __USE_GNU
+# define __need_sigevent_t
+# include <bits/siginfo.h>
+#endif
+
 #include <bits/netdb.h>
 
 /* Absolute file name for network data base files.  */
@@ -412,23 +417,48 @@ struct addrinfo
   struct addrinfo *ai_next;	/* Pointer to next in list.  */
 };
 
+# ifdef __USE_GNU
+/* Structure used as control block for asynchronous lookup.  */
+struct gaicb
+{
+  const char *ar_name;		/* Name to look up.  */
+  const char *ar_service;	/* Service name.  */
+  const struct addrinfo *ar_request; /* Additional request specification.  */
+  struct addrinfo *ar_result;	/* Pointer to result.  */
+  /* The following are internal elements.  */
+  int __return;
+  int __unused[5];
+};
+
+/* Lookup mode.  */
+#  define GAI_WAIT	0
+#  define GAI_NOWAIT	1
+# endif
+
 /* Possible values for `ai_flags' field in `addrinfo' structure.  */
 # define AI_PASSIVE	0x0001	/* Socket address is intended for `bind'.  */
 # define AI_CANONNAME	0x0002	/* Request for canonical name.  */
 # define AI_NUMERICHOST	0x0004	/* Don't use name resolution.  */
 
 /* Error values for `getaddrinfo' function.  */
-# define EAI_BADFLAGS	-1	/* Invalid value for `ai_flags' field.  */
-# define EAI_NONAME	-2	/* NAME or SERVICE is unknown.  */
-# define EAI_AGAIN	-3	/* Temporary failure in name resolution.  */
-# define EAI_FAIL	-4	/* Non-recoverable failure in name res.  */
-# define EAI_NODATA	-5	/* No address associated with NAME.  */
-# define EAI_FAMILY	-6	/* `ai_family' not supported.  */
-# define EAI_SOCKTYPE	-7	/* `ai_socktype' not supported.  */
-# define EAI_SERVICE	-8	/* SERVICE not supported for `ai_socktype'.  */
-# define EAI_ADDRFAMILY	-9	/* Address family for NAME not supported.  */
-# define EAI_MEMORY	-10	/* Memory allocation failure.  */
-# define EAI_SYSTEM	-11	/* System error returned in `errno'.  */
+# define EAI_BADFLAGS	  -1	/* Invalid value for `ai_flags' field.  */
+# define EAI_NONAME	  -2	/* NAME or SERVICE is unknown.  */
+# define EAI_AGAIN	  -3	/* Temporary failure in name resolution.  */
+# define EAI_FAIL	  -4	/* Non-recoverable failure in name res.  */
+# define EAI_NODATA	  -5	/* No address associated with NAME.  */
+# define EAI_FAMILY	  -6	/* `ai_family' not supported.  */
+# define EAI_SOCKTYPE	  -7	/* `ai_socktype' not supported.  */
+# define EAI_SERVICE	  -8	/* SERVICE not supported for `ai_socktype'.  */
+# define EAI_ADDRFAMILY	  -9	/* Address family for NAME not supported.  */
+# define EAI_MEMORY	  -10	/* Memory allocation failure.  */
+# define EAI_SYSTEM	  -11	/* System error returned in `errno'.  */
+# ifdef __USE_GNU
+#  define EAI_INPROGRESS  -100	/* Processing request in progress.  */
+#  define EAI_CANCELED	  -101	/* Request canceled.  */
+#  define EAI_NOTCANCELED -102	/* Request not canceled.  */
+#  define EAI_ALLDONE	  -103	/* All requests done.  */
+#  define EAI_INTR	  -104	/* Interrupted by a signal.  */
+# endif
 
 # define NI_MAXHOST      1025
 # define NI_MAXSERV      32
@@ -458,6 +488,26 @@ extern int getnameinfo (__const struct sockaddr *__restrict __sa,
 			socklen_t __hostlen, char *__restrict __serv,
 			socklen_t __servlen, unsigned int __flags) __THROW;
 
+# ifdef __USE_GNU
+/* Enqueue ENT requests from the LIST.  If MODE is GAI_WAIT wait until all
+   requests are handled.  If WAIT is GAI_NOWAIT return immediately after
+   queueing the requests and signal completion according to SIG.  */
+extern int getaddrinfo_a (int __mode, struct gaicb *__list[__restrict_arr],
+			  int __ent, struct sigevent *__restrict __sig)
+     __THROW;
+
+/* Suspend execution of the thread until at least one of the ENT requests
+   in LIST is handled.  If TIMEOUT is not a null pointer it specifies the
+   longest time the function keeps waiting before returning with an error.  */
+extern int gai_suspend (__const struct gaicb *__const __list[], int __ent,
+			const struct timespec *__timeout) __THROW;
+
+/* Get the error status of the request REQ.  */
+extern int gai_error (struct gaicb *__req) __THROW;
+
+/* Cancel the requests associated with GAICBP.  */
+extern int gai_cancel (struct gaicb *__gaicbp) __THROW;
+# endif	/* GNU */
 #endif	/* POSIX */
 
 __END_DECLS