chiark / gitweb /
New poll(2) stuff etc. Does not work yet, but compiles.
authorian <ian>
Mon, 5 Jul 1999 01:50:58 +0000 (01:50 +0000)
committerian <ian>
Mon, 5 Jul 1999 01:50:58 +0000 (01:50 +0000)
1  2 
acconfig.h
changelog
configure
configure.in
src/adns.h
src/adns.make
src/config.h.in
src/event.c
src/internal.h
src/poll.c

diff --cc acconfig.h
index 73a7fe372fe7083dd2c36724e62d0bc2dcacdeb2,73a7fe372fe7083dd2c36724e62d0bc2dcacdeb2..16672a6689bb91e564fb3828201e8332f2c21b43
  
  /* Use the definitions: */
  
++#ifdef HAVE_POLL
++#include <sys/poll.h>
++#else
++struct pollfd { int fd; short events; short revents; };
++#define POLLIN  1
++#define POLLPRI 2
++#define POLLOUT 4
++#endif
++
  /* GNU C attributes. */
  #ifndef FUNCATTR
  #ifdef HAVE_GNUC25_ATTRIB
diff --cc changelog
index d83d3c2073f5ab6e89558f7795a7b4d8667b50d8,e1fa705259536a2d5c319c1ea9f43aa0bf7f36bf..207c322f1d0f1277c81f4ff8624a0538456bf43d
+++ b/changelog
@@@ -1,41 -1,3 +1,47 @@@
 +adns (0.3) unstable; urgency=low
 +
++  Incompatible changes:
++  * Low adns_status values (below adns_s_max_tempfail) renumbered to make
++    room for future locally-induced and locally-detected errors.
++  * Event loop functions for use by select(2) renamed and tidied up.
++  
 +  Features / improvements:
 +  * New adns_errabbrev() for getting status abbreviation strings.
 +  * regress/checkall prints summary list of failed tests, if any.
++  * Event loop functions for poll(2), and some raw variants.
 +  
 +  Bugfixes:
 +  * Non-RFC822 mailbox `domain' formatting now works, and clarified.
 +  * Rejection of bad characters in domains (without quoteok) works.
 +  * Clean up parents from adns->childw (otherwise would abort/segfault).
 +  * In adnstest, allocate enough space for, and terminate, query types.
 +  * In adnstest, don't print errno values as adns_status values.
 + 
 +  * Added TODO file.
 +
 + --
 +
 +adns (0.2) experimental; urgency=low
 +
 +  Portability fixes for compilation on various platforms:
 +  * Include <sys/socket.h> and <netinet/in.h> in files with <arpa/inet.h>.
 +  * Don't use GCC union assignment feature (.rrs=0 => .rrs.untyped=0).
 +  * Explictly cast things to [const] struct sockaddr* in syscall args.
 +  * Check whether we need -lsocket.
 +  * Include <sys/times.h> in a few more files.
 +  * Include <unistd.h> and <sys/time.h> for select.
 +  * Look for inet_aton and inet_ntoa (in -lnsl and -lsocket).
 +  * LDLIBS removed from dependency lists (some makes don't support this).
 +  * An `ambiguous else' warning from some compilers in types.c is removed.
 +
 +  Other changes:
 +  * Added COPYING (copy of the GPL).
 +  * Regression test failure output improved.
 +  * Missing targets in regress/Makefile.in added.
 +  * Regression test doesn't rely on value of fcntl flags eg O_NONBLOCK.
 +
 + -- Ian Jackson <ian@davenant.greenend.org.uk>  Thu, 20 May 1999 00:27:32 +0100
 +
  adns (0.1) experimental; urgency=low
  
    * Initial public alpha release.
diff --cc configure
index 4bf677b15ae5562183b00b4daa4a201b4861add6,5cc73d5044f229722d9c478efe05337ab30ec1f9..5df8065a5dbf76f1a43f0d0990a7edaea4f0a8be
+++ b/configure
  fi
  
  
- echo "configure:725: checking for socket" >&5
++for ac_func in poll
++do
++echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
++echo "configure:726: checking for $ac_func" >&5
++if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
++  echo $ac_n "(cached) $ac_c" 1>&6
++else
++  cat > conftest.$ac_ext <<EOF
++#line 731 "configure"
++#include "confdefs.h"
++/* System header to define __stub macros and hopefully few prototypes,
++    which can conflict with char $ac_func(); below.  */
++#include <assert.h>
++/* Override any gcc2 internal prototype to avoid an error.  */
++/* We use char because int might match the return type of a gcc2
++    builtin and then its argument prototype would still apply.  */
++char $ac_func();
++
++int main() {
++
++/* The GNU C library defines this for functions which it implements
++    to always fail with ENOSYS.  Some functions are actually named
++    something starting with __ and the normal name is an alias.  */
++#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
++choke me
++#else
++$ac_func();
++#endif
++
++; return 0; }
++EOF
++if { (eval echo configure:754: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++  rm -rf conftest*
++  eval "ac_cv_func_$ac_func=yes"
++else
++  echo "configure: failed program was:" >&5
++  cat conftest.$ac_ext >&5
++  rm -rf conftest*
++  eval "ac_cv_func_$ac_func=no"
++fi
++rm -f conftest*
++fi
++
++if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
++  echo "$ac_t""yes" 1>&6
++    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
++  cat >> confdefs.h <<EOF
++#define $ac_tr_func 1
++EOF
++ 
++else
++  echo "$ac_t""no" 1>&6
++fi
++done
++
 +
 + echo $ac_n "checking for socket""... $ac_c" 1>&6
- #line 730 "configure"
++echo "configure:780: checking for socket" >&5
 +if eval "test \"`echo '$''{'ac_cv_func_socket'+set}'`\" = set"; then
 +  echo $ac_n "(cached) $ac_c" 1>&6
 +else
 +  cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:753: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 785 "configure"
 +#include "confdefs.h"
 +/* System header to define __stub macros and hopefully few prototypes,
 +    which can conflict with char socket(); below.  */
 +#include <assert.h>
 +/* Override any gcc2 internal prototype to avoid an error.  */
 +/* We use char because int might match the return type of a gcc2
 +    builtin and then its argument prototype would still apply.  */
 +char socket();
 +
 +int main() {
 +
 +/* The GNU C library defines this for functions which it implements
 +    to always fail with ENOSYS.  Some functions are actually named
 +    something starting with __ and the normal name is an alias.  */
 +#if defined (__stub_socket) || defined (__stub___socket)
 +choke me
 +#else
 +socket();
 +#endif
 +
 +; return 0; }
 +EOF
- echo "configure:772: checking for socket in -lsocket" >&5
++if { (eval echo configure:808: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
 +  rm -rf conftest*
 +  eval "ac_cv_func_socket=yes"
 +else
 +  echo "configure: failed program was:" >&5
 +  cat conftest.$ac_ext >&5
 +  rm -rf conftest*
 +  eval "ac_cv_func_socket=no"
 +fi
 +rm -f conftest*
 +fi
 +
 +if eval "test \"`echo '$ac_cv_func_'socket`\" = yes"; then
 +  echo "$ac_t""yes" 1>&6
 +  :
 +else
 +  echo "$ac_t""no" 1>&6
 +
 +  echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
- #line 780 "configure"
++echo "configure:827: checking for socket in -lsocket" >&5
 +ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'`
 +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
 +  echo $ac_n "(cached) $ac_c" 1>&6
 +else
 +  ac_save_LIBS="$LIBS"
 +LIBS="-lsocket  $LIBS"
 +cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:791: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 835 "configure"
 +#include "confdefs.h"
 +/* Override any gcc2 internal prototype to avoid an error.  */
 +/* We use char because int might match the return type of a gcc2
 +    builtin and then its argument prototype would still apply.  */
 +char socket();
 +
 +int main() {
 +socket()
 +; return 0; }
 +EOF
- echo "configure:827: checking for inet_ntoa" >&5
++if { (eval echo configure:846: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
 +  rm -rf conftest*
 +  eval "ac_cv_lib_$ac_lib_var=yes"
 +else
 +  echo "configure: failed program was:" >&5
 +  cat conftest.$ac_ext >&5
 +  rm -rf conftest*
 +  eval "ac_cv_lib_$ac_lib_var=no"
 +fi
 +rm -f conftest*
 +LIBS="$ac_save_LIBS"
 +
 +fi
 +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
 +  echo "$ac_t""yes" 1>&6
 +    ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \
 +    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
 +  cat >> confdefs.h <<EOF
 +#define $ac_tr_lib 1
 +EOF
 +
 +  LIBS="-lsocket $LIBS"
 +
 +else
 +  echo "$ac_t""no" 1>&6
 +
 +    { echo "configure: error: cannot find library function socket" 1>&2; exit 1; }
 +  
 +fi
 +
 + 
 +fi
 +
 +
 +
 + echo $ac_n "checking for inet_ntoa""... $ac_c" 1>&6
- #line 832 "configure"
++echo "configure:882: checking for inet_ntoa" >&5
 +if eval "test \"`echo '$''{'ac_cv_func_inet_ntoa'+set}'`\" = set"; then
 +  echo $ac_n "(cached) $ac_c" 1>&6
 +else
 +  cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:855: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 887 "configure"
 +#include "confdefs.h"
 +/* System header to define __stub macros and hopefully few prototypes,
 +    which can conflict with char inet_ntoa(); below.  */
 +#include <assert.h>
 +/* Override any gcc2 internal prototype to avoid an error.  */
 +/* We use char because int might match the return type of a gcc2
 +    builtin and then its argument prototype would still apply.  */
 +char inet_ntoa();
 +
 +int main() {
 +
 +/* The GNU C library defines this for functions which it implements
 +    to always fail with ENOSYS.  Some functions are actually named
 +    something starting with __ and the normal name is an alias.  */
 +#if defined (__stub_inet_ntoa) || defined (__stub___inet_ntoa)
 +choke me
 +#else
 +inet_ntoa();
 +#endif
 +
 +; return 0; }
 +EOF
- echo "configure:874: checking for inet_ntoa in -lnsl" >&5
++if { (eval echo configure:910: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
 +  rm -rf conftest*
 +  eval "ac_cv_func_inet_ntoa=yes"
 +else
 +  echo "configure: failed program was:" >&5
 +  cat conftest.$ac_ext >&5
 +  rm -rf conftest*
 +  eval "ac_cv_func_inet_ntoa=no"
 +fi
 +rm -f conftest*
 +fi
 +
 +if eval "test \"`echo '$ac_cv_func_'inet_ntoa`\" = yes"; then
 +  echo "$ac_t""yes" 1>&6
 +  :
 +else
 +  echo "$ac_t""no" 1>&6
 +
 +  echo $ac_n "checking for inet_ntoa in -lnsl""... $ac_c" 1>&6
- #line 882 "configure"
++echo "configure:929: checking for inet_ntoa in -lnsl" >&5
 +ac_lib_var=`echo nsl'_'inet_ntoa | sed 'y%./+-%__p_%'`
 +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
 +  echo $ac_n "(cached) $ac_c" 1>&6
 +else
 +  ac_save_LIBS="$LIBS"
 +LIBS="-lnsl  $LIBS"
 +cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:893: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 937 "configure"
 +#include "confdefs.h"
 +/* Override any gcc2 internal prototype to avoid an error.  */
 +/* We use char because int might match the return type of a gcc2
 +    builtin and then its argument prototype would still apply.  */
 +char inet_ntoa();
 +
 +int main() {
 +inet_ntoa()
 +; return 0; }
 +EOF
- echo "configure:930: checking for inet_aton" >&5
++if { (eval echo configure:948: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
 +  rm -rf conftest*
 +  eval "ac_cv_lib_$ac_lib_var=yes"
 +else
 +  echo "configure: failed program was:" >&5
 +  cat conftest.$ac_ext >&5
 +  rm -rf conftest*
 +  eval "ac_cv_lib_$ac_lib_var=no"
 +fi
 +rm -f conftest*
 +LIBS="$ac_save_LIBS"
 +
 +fi
 +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
 +  echo "$ac_t""yes" 1>&6
 +    ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/[^a-zA-Z0-9_]/_/g' \
 +    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
 +  cat >> confdefs.h <<EOF
 +#define $ac_tr_lib 1
 +EOF
 +
 +  LIBS="-lnsl $LIBS"
 +
 +else
 +  echo "$ac_t""no" 1>&6
 +
 +    { echo "configure: error: cannot find library function inet_ntoa" 1>&2; exit 1; }
 +  
 +fi
 +
 + 
 +fi
 +
 +
 +
 +
 + echo $ac_n "checking for inet_aton""... $ac_c" 1>&6
- #line 935 "configure"
++echo "configure:985: checking for inet_aton" >&5
 +if eval "test \"`echo '$''{'ac_cv_func_inet_aton'+set}'`\" = set"; then
 +  echo $ac_n "(cached) $ac_c" 1>&6
 +else
 +  cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:958: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 990 "configure"
 +#include "confdefs.h"
 +/* System header to define __stub macros and hopefully few prototypes,
 +    which can conflict with char inet_aton(); below.  */
 +#include <assert.h>
 +/* Override any gcc2 internal prototype to avoid an error.  */
 +/* We use char because int might match the return type of a gcc2
 +    builtin and then its argument prototype would still apply.  */
 +char inet_aton();
 +
 +int main() {
 +
 +/* The GNU C library defines this for functions which it implements
 +    to always fail with ENOSYS.  Some functions are actually named
 +    something starting with __ and the normal name is an alias.  */
 +#if defined (__stub_inet_aton) || defined (__stub___inet_aton)
 +choke me
 +#else
 +inet_aton();
 +#endif
 +
 +; return 0; }
 +EOF
- echo "configure:977: checking for inet_aton in -lresolv" >&5
++if { (eval echo configure:1013: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
 +  rm -rf conftest*
 +  eval "ac_cv_func_inet_aton=yes"
 +else
 +  echo "configure: failed program was:" >&5
 +  cat conftest.$ac_ext >&5
 +  rm -rf conftest*
 +  eval "ac_cv_func_inet_aton=no"
 +fi
 +rm -f conftest*
 +fi
 +
 +if eval "test \"`echo '$ac_cv_func_'inet_aton`\" = yes"; then
 +  echo "$ac_t""yes" 1>&6
 +  :
 +else
 +  echo "$ac_t""no" 1>&6
 +
 +  echo $ac_n "checking for inet_aton in -lresolv""... $ac_c" 1>&6
- #line 985 "configure"
++echo "configure:1032: checking for inet_aton in -lresolv" >&5
 +ac_lib_var=`echo resolv'_'inet_aton | sed 'y%./+-%__p_%'`
 +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
 +  echo $ac_n "(cached) $ac_c" 1>&6
 +else
 +  ac_save_LIBS="$LIBS"
 +LIBS="-lresolv  $LIBS"
 +cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:996: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 1040 "configure"
 +#include "confdefs.h"
 +/* Override any gcc2 internal prototype to avoid an error.  */
 +/* We use char because int might match the return type of a gcc2
 +    builtin and then its argument prototype would still apply.  */
 +char inet_aton();
 +
 +int main() {
 +inet_aton()
 +; return 0; }
 +EOF
++if { (eval echo configure:1051: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
 +  rm -rf conftest*
 +  eval "ac_cv_lib_$ac_lib_var=yes"
 +else
 +  echo "configure: failed program was:" >&5
 +  cat conftest.$ac_ext >&5
 +  rm -rf conftest*
 +  eval "ac_cv_lib_$ac_lib_var=no"
 +fi
 +rm -f conftest*
 +LIBS="$ac_save_LIBS"
 +
 +fi
 +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
 +  echo "$ac_t""yes" 1>&6
 +  
 + LIBS="-lresolv $LIBS";
 + echo "configure: warning: inet_aton is in libresolv, urgh.  Must use -lresolv." 1>&2
 +
 +else
 +  echo "$ac_t""no" 1>&6
 +
 +    { echo "configure: error: cannot find library function inet_aton" 1>&2; exit 1; }
 +  
 +fi
 +
 + 
 +fi
 +
 +
 +
 +
   
   echo $ac_n "checking __attribute__((,,))""... $ac_c" 1>&6
- echo "configure:1030: checking __attribute__((,,))" >&5
 -echo "configure:725: checking __attribute__((,,))" >&5
++echo "configure:1085: checking __attribute__((,,))" >&5
   if eval "test \"`echo '$''{'adns_cv_c_attribute_supported'+set}'`\" = set"; then
    echo $ac_n "(cached) $ac_c" 1>&6
  else
    
    cat > conftest.$ac_ext <<EOF
- #line 1036 "configure"
 -#line 731 "configure"
++#line 1091 "configure"
  #include "confdefs.h"
  
  int main() {
  extern int testfunction(int x) __attribute__((,,))
  ; return 0; }
  EOF
- if { (eval echo configure:1043: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
 -if { (eval echo configure:738: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
++if { (eval echo configure:1098: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
    rm -rf conftest*
    adns_cv_c_attribute_supported=yes
  else
  
     
   echo $ac_n "checking __attribute__((noreturn))""... $ac_c" 1>&6
- echo "configure:1065: checking __attribute__((noreturn))" >&5
 -echo "configure:760: checking __attribute__((noreturn))" >&5
++echo "configure:1120: checking __attribute__((noreturn))" >&5
   if eval "test \"`echo '$''{'adns_cv_c_attribute_noreturn'+set}'`\" = set"; then
    echo $ac_n "(cached) $ac_c" 1>&6
  else
    
    cat > conftest.$ac_ext <<EOF
- #line 1071 "configure"
 -#line 766 "configure"
++#line 1126 "configure"
  #include "confdefs.h"
  
  int main() {
  extern int testfunction(int x) __attribute__((noreturn))
  ; return 0; }
  EOF
- if { (eval echo configure:1078: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
 -if { (eval echo configure:773: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
++if { (eval echo configure:1133: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
    rm -rf conftest*
    adns_cv_c_attribute_noreturn=yes
  else
  
     
   echo $ac_n "checking __attribute__((const))""... $ac_c" 1>&6
- echo "configure:1105: checking __attribute__((const))" >&5
 -echo "configure:800: checking __attribute__((const))" >&5
++echo "configure:1160: checking __attribute__((const))" >&5
   if eval "test \"`echo '$''{'adns_cv_c_attribute_const'+set}'`\" = set"; then
    echo $ac_n "(cached) $ac_c" 1>&6
  else
    
    cat > conftest.$ac_ext <<EOF
- #line 1111 "configure"
 -#line 806 "configure"
++#line 1166 "configure"
  #include "confdefs.h"
  
  int main() {
  extern int testfunction(int x) __attribute__((const))
  ; return 0; }
  EOF
- if { (eval echo configure:1118: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
 -if { (eval echo configure:813: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
++if { (eval echo configure:1173: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
    rm -rf conftest*
    adns_cv_c_attribute_const=yes
  else
  
     
   echo $ac_n "checking __attribute__((format...))""... $ac_c" 1>&6
- echo "configure:1145: checking __attribute__((format...))" >&5
 -echo "configure:840: checking __attribute__((format...))" >&5
++echo "configure:1200: checking __attribute__((format...))" >&5
   if eval "test \"`echo '$''{'adns_cv_attribute_format'+set}'`\" = set"; then
    echo $ac_n "(cached) $ac_c" 1>&6
  else
    
    cat > conftest.$ac_ext <<EOF
- #line 1151 "configure"
 -#line 846 "configure"
++#line 1206 "configure"
  #include "confdefs.h"
  
  int main() {
  extern int testfunction(char *y, ...) __attribute__((format(printf,1,2)))
  ; return 0; }
  EOF
- if { (eval echo configure:1158: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
 -if { (eval echo configure:853: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
++if { (eval echo configure:1213: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
    rm -rf conftest*
    adns_cv_attribute_format=yes
  else
diff --cc configure.in
index 0267a37fcccb2c9b26be309d4d8f9e05683f8cf6,8d40cc1719d59b46f671d6226b318f3710ca7ad8..6e342c07cd35c47ad279042a5452209d12dc0294
@@@ -20,15 -20,6 +20,16 @@@ AC_INIT(src/adns.h
  AC_CONFIG_HEADER(src/config.h)
  AC_PROG_CC
  AC_PROG_RANLIB
 +
++AC_CHECK_FUNCS(poll)
 +ADNS_C_GETFUNC(socket,socket)
 +ADNS_C_GETFUNC(inet_ntoa,nsl)
 +
 +ADNS_C_GETFUNC(inet_aton,resolv,[
 + LIBS="-lresolv $LIBS";
 + AC_MSG_WARN([inet_aton is in libresolv, urgh.  Must use -lresolv.])
 +])
 +
  ADNS_C_GCCATTRIB
  
  AC_SUBST(WARNS)
diff --cc src/adns.h
index 88b51f8dc8a58f48baf4fb65fab813b777e351d5,d3ab7eda9e9b92eb7dccbeedda306dc356a9c649..ce92c8b2d614da3ae6de048b9e9208a0e878504c
@@@ -111,6 -109,6 +111,9 @@@ typedef enum 
    /* locally induced errors */
    adns_s_nomemory,
    adns_s_unknownrrtype,
++  adns_s_systemfail,
++
++  adns_s_max_localfail= 29,
    
    /* remotely induced errors, detected locally */
    adns_s_timeout,
    adns_s_norecurse,
    adns_s_invalidresponse,
    adns_s_unknownformat,
++
++  adns_s_max_remotefail= 59,
    
    /* remotely induced errors, reported by remote server to us */
    adns_s_rcodeservfail,
    adns_s_rcodenotimplemented,
    adns_s_rcoderefused,
    adns_s_rcodeunknown,
--  
++
    adns_s_max_tempfail= 99,
  
    /* remote configuration errors */
@@@ -228,15 -225,15 +233,25 @@@ typedef struct 
   *  If the call is successful, *query_io, *answer_r, and *context_r
   *  will all be set.
   * Errors:
-- *  Return values are 0 or an errno value;
-- *  Seriously fatal system errors (eg, failure to create sockets,
-- *  malloc failure, etc.) return errno values;
-- *  Other errors (nameserver failure, timed out connections, &c)
-- *  are returned in the status field of the answer.  If status is
-- *  nonzero then nrrs will be 0, otherwise it will be >0.
-- *  type will always be the type requested;
-- *  If no (appropriate) requests are done adns_check returns EWOULDBLOCK;
-- *  If no (appropriate) requests are outstanding adns_query and adns_wait return ESRCH;
++ *  Return values are 0 or an errno value.
++ *
++ *  For _init, _init_strcfg, _submit and _synchronous, system errors
++ *  (eg, failure to create sockets, malloc failure, etc.) return errno
++ *  values.
++ * 
++ *  For _wait and _check failures are reported in the answer
++ *  structure, and only 0, ESRCH or (for _check) EWOULDBLOCK is
++ *  returned: if no (appropriate) requests are done adns_check returns
++ *  EWOULDBLOCK; if no (appropriate) requests are outstanding both
++ *  adns_query and adns_wait return ESRCH.
++ *
++ *  Additionally, _wait can return EINTR if you set adns_if_eintr.
++ *
++ *  All other errors (nameserver failure, timed out connections, &c)
++ *  are returned in the status field of the answer.  After a
++ *  successful _wait or _check, if status is nonzero then nrrs will be
++ *  0, otherwise it will be >0.  type will always be the type
++ *  requested.
   */
  
  int adns_init(adns_state *newstate_r, adns_initflags flags,
@@@ -252,8 -249,8 +267,8 @@@ int adns_synchronous(adns_state ads
                     adns_answer **answer_r);
  
  /* NB: if you set adns_if_noautosys then _submit and _check do not
-- * make any system calls; you must use adns_callback (possibly after
-- * adns_interest) to actually get things to happen.
++ * make any system calls; you must use some of the asynch-io event
++ * processing functions to actually get things to happen.
   */
  
  int adns_submit(adns_state ads,
@@@ -275,36 -272,67 +290,37 @@@ int adns_wait(adns_state ads
  
  void adns_cancel(adns_query query);
  
--void adns_finish(adns_state);
 -/* You may call this even if you have queries outstanding;
 - * they will be cancelled.
++/* The adns_query you get back from _submit is valid (ie, can be
++ * legitimately passed into adns functions) until it is returned by
++ * adns_check or adns_wait, or passed to adns_cancel.  After that it
++ * must not be used.  You can rely on it not being reused until the
++ * first adns_submit or _transact call using the same adns_state after
++ * it became invalid, so you may compare it for equality with other
++ * query handles until you next call _query or _transact.
+  */
 -int adns_callback(adns_state, int maxfd, const fd_set *readfds, const fd_set *writefds,
 -                const fd_set *exceptfds);
 -/* Gives adns flow-of-control for a bit.  This will never block.
 - * If maxfd == -1 then adns will check (make nonblocking system calls on)
 - * all of its own filedescriptors; otherwise it will only use those
 - * < maxfd and specified in the fd_set's, as if select had returned them.
 - * Other fd's may be in the fd_sets, and will be ignored.
 - * _callback returns how many adns fd's were in the various sets, so
 - * you can tell if your select handling code has missed something and is going awol.
 - *
 - * May also return -1 if a critical syscall failed, setting errno.
++void adns_finish(adns_state ads);
 +/* You may call this even if you have queries outstanding;
 + * they will be cancelled.
   */
  
- int adns_callback(adns_state, int maxfd, const fd_set *readfds, const fd_set *writefds,
-                 const fd_set *exceptfds);
- /* Gives adns flow-of-control for a bit.  This will never block.
-  * If maxfd == -1 then adns will check (make nonblocking system calls on)
-  * all of its own filedescriptors; otherwise it will only use those
-  * < maxfd and specified in the fd_set's, as if select had returned them.
-  * Other fd's may be in the fd_sets, and will be ignored.
-  * _callback returns how many adns fd's were in the various sets, so
-  * you can tell if your select handling code has missed something and is going awol.
-  *
-  * May also return -1 if a critical syscall failed, setting errno.
 -void adns_interest(adns_state, int *maxfd_io, fd_set *readfds_io,
 -                 fd_set *writefds_io, fd_set *exceptfds_io,
 -                 struct timeval **tv_mod, struct timeval *tv_buf);
 -/* Find out file descriptors adns is interested in, and when it
 - * would like the opportunity to time something out.  If you do not plan to
 - * block then tv_mod may be 0.  Otherwise, tv_mod may point to 0 meaning
 - * you have no timeout of your own, in which case tv_buf must be non-null and
 - * _interest may fill it in and set *tv_mod=tv_buf.
 - * readfds, writefds, exceptfds and maxfd may not be 0.
-- */
  
- void adns_interest(adns_state, int *maxfd_io, fd_set *readfds_io,
-                  fd_set *writefds_io, fd_set *exceptfds_io,
-                  struct timeval **tv_mod, struct timeval *tv_buf);
- /* Find out file descriptors adns is interested in, and when it
-  * would like the opportunity to time something out.  If you do not plan to
-  * block then tv_mod may be 0.  Otherwise, tv_mod may point to 0 meaning
-  * you have no timeout of your own, in which case tv_buf must be non-null and
-  * _interest may fill it in and set *tv_mod=tv_buf.
-  * readfds, writefds, exceptfds and maxfd may not be 0.
 -struct pollfd *adns_pollfds(adns_state, struct pollfd *buf, int *len_io, int *timeout);
 -/* Like adns_interest, but for use with poll(2).  The return value
 - * will be a pointer to an array of struct pollfd, one for each of
 - * adns's fds, and *len_io will be set to the number of file
 - * descriptors.
 - *
 - * Each descriptor's entry will have a revents==0, and events will be
 - * a combination of one or more of POLLIN, POLLOUT and POLLERR.
 - *
 - * *timeout will be adjusted to ensure that it is no more than the
 - * desired timeout (with the usual rule for poll that <0 stands for
 - * infinity).  *timeout may be 0 in which case no timeout information
 - * will be returned.
 - *
 - * If *len_io is nonnegative then adns_pollfds will write to buffer
 - * provided by the caller in buf.  If it is too small then *len_io
 - * will be updated to say how large it would have needed to be and
 - * adns_pollfds will fail with ENOSPC.
 - *
 - * If *len_io is -1 then buf should be 0, and adns_pollfds will use some
 - * memory belonging to the adns_state, which may be overwritten on subsequent
 - * calls.
 - *
 - * When you come out of poll(2), you should probably call adns_callback
 - * with maxfd==-1.
++struct adns_query adns_forallqueries_begin(adns_state ads, void **context_r);
++struct adns_query adns_forallqueries_next(adns_state ads, adns_query, void **context_r);
++/* Iterator functions, which you can use to loop over the outstanding
++ * (submitted but not yet successfuly checked/waited) queries.  Each
++ * function returns a query handle and a corresponding context pointer,
++ * or returns 0 setting *context_r to 0 if there are no (more) queries.
++ * There is no need to explicitly finish an iteration.  context_r may be 0.
+  *
 - * On systems without poll(2) this function will return ENOSYS.
++ * IMPORTANT: you MUST NOT call ANY other adns function with the same
++ * adns_state, or with a query in the same adns_state, while you are
++ * doing one of these iterations.  After such a call the iterator
++ * value has undefined meaning and must not be used.
   */
  
--/* Example expected/legal calling sequences:
++/*
++ * Example expected/legal calling sequence for submit/check/wait:
   *  adns_init
   *  adns_submit 1
   *  adns_submit 2
   *  adns_wait 3
   *  ....
   *  adns_finish
++ */
++
++/*
++ * Entrypoints for generic asynch io:
++ * (these entrypoints are not very useful except in combination with *
++ * some of the other I/O model calls which can tell you which fds to
++ * be interested in):
++ *
++ * Note that any adns call may cause adns to open and close fds, so
++ * you must call beforeselect or beforepoll again just before
++ * blocking, or you may not have an up-to-date list of it's fds.
++ */
++
++int adns_processany(adns_state ads);
++/* Gives adns flow-of-control for a bit.  This will never block, and
++ * can be used with any threading/asynch-io model.  If some error
++ * occurred which might cause an event loop to spin then the errno
++ * value is returned.
++ */
++
++int adns_processreadable(adns_state ads, int fd, const struct timeval *now);
++int adns_processwriteable(adns_state ads, int fd, const struct timeval *now);
++int adns_processexceptional(adns_state ads, int fd, const struct timeval *now);
++/* Gives adns flow-of-control so that it can process incoming data
++ * from, or send outgoing data via, fd.  Very like _processany.  If it
++ * returns zero then fd will no longer be readable or writeable
++ * (unless of course more data has arrived since).  adns will _only_
++ * that fd and only in the manner specified, regardless of whether
++ * adns_if_noautosys was specified.
++ *
++ * adns_processexceptional should be called when select(2) reports an
++ * exceptional condition, or poll(2) reports POLLPRI.
++ *
++ * It is fine to call _processreabable or _processwriteable when the
++ * fd is not ready, or with an fd that doesn't belong to adns; it will
++ * then just return 0.
++ *
++ * If some error occurred which might prevent an event loop to spin
++ * then the errno value is returned.
++ */
++
++void adns_processtimeouts(adns_state ads, const struct timeval *now);
++/* Gives adns flow-of-control so that it can process any timeouts
++ * which might have happened.  Very like _processreadable/writeable.
++ *
++ * now may be 0; if it isn't, *now must be the current time, recently
++ * obtained from gettimeofday.
++ */
++
++void adns_firsttimeout(adns_state ads,
++                     struct timeval **tv_mod, struct timeval *tv_buf,
++                     struct timeval now);
++/* Asks adns when it would first like the opportunity to time
++ * something out.  now must be the current time, from gettimeofday.
++ * 
++ * If tv_mod points to 0 then tv_buf must be non-null, and
++ * _firsttimeout will fill in *tv_buf with the time until the first
++ * timeout, and make *tv_mod point to tv_buf.  If adns doesn't have
++ * anything that might need timing out it will leave *tv_mod as 0.
++ *
++ * If *tv_mod is not 0 then tv_buf is not used.  adns will update
++ * *tv_mod if it has any earlier timeout, and leave it alone if it
++ * doesn't.
++ *
++ * This call will not actually do any I/O, or change the fds that adns
++ * is using.  It always succeeds and never blocks.
++ */
++
++void adns_globalsystemfailure(adns_state ads);
++/* If serious problem(s) happen which globally affect your ability to
++ * interact properly with adns, or adns's ability to function
++ * properly, you or adns can call this function.
++ *
++ * All currently outstanding queries will be made to fail with
++ * adns_s_systemfail, and adns will close any stream sockets it has
++ * open.
++ *
++ * This is used by adns, for example, if gettimeofday() fails.
++ * Without this the program's event loop might start to spin !
++ *
++ * This call will never block.
++ */
++
++/*
++ * Entrypoints for select-loop based asynch io:
++ */
++
++void adns_beforeselect(adns_state ads, int *maxfd, fd_set *readfds,
++                     fd_set *writefds, fd_set *exceptfds,
++                     struct timeval **tv_mod, struct timeval *tv_buf,
++                     const struct timeval *now);
++/* Find out file descriptors adns is interested in, and when it would
++ * like the opportunity to time something out.  If you do not plan to
++ * block then tv_mod may be 0.  Otherwise, tv_mod and tv_buf are as
++ * for adns_firsttimeout.  readfds, writefds, exceptfds and maxfd_io may
++ * not be 0.
++ *
++ * If *now is not 0 then this will never actually do any I/O, or
++ * change the fds that adns is using or the timeouts it wants.  In any
++ * case it won't block.
++ */
++
++void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
++                    const fd_set *writefds, const fd_set *exceptfds,
++                    const struct timeval *now);
++/* Gives adns flow-of-control for a bit; intended for use after
++ * select.  This is just a fancy way of calling adns_processreadable/
++ * writeable/timeouts as appropriate, as if select had returned the
++ * data being passed.  Always succeeds.
++ */
++
++/*
++ * Example calling sequence:
   *
   *  adns_init _noautosys
   *  loop {
-- *   adns_interest
++ *   adns_beforeselect
   *   select
-- *   adns_callback
++ *   adns_afterselect
   *   ...
   *   adns_submit / adns_check
   *   ...
   *  }
   */
  
++/*
++ * Entrypoints for poll-loop based asynch io:
++ */
++
++struct pollfd;
++/* In case your system doesn't have it or you forgot to include
++ * <sys/poll.h>, to stop the following declarations from causing
++ * problems.  If your system doesn't have poll then the following
++ * entrypoints will not be defined in libadns.  Sorry !
++ */
++
++int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io,
++                  const struct timeval *now);
++/* Finds out which fd's adns is interested in, and when it would like
++ * to be able to time things out.  This is in a form suitable for use
++ * with poll(2).
++ * 
++ * On entry, usually fds should point to at least *nfds_io structs.
++ * adns will fill up to that many structs will information for poll,
++ * and record in *nfds_io how many structs it filled.  If it wants to
++ * listen for more structs then *nfds_io will be set to the number
++ * required and _beforepoll will return ERANGE.
++ *
++ * NOTE that if now is 0, adns may acquire additional fds from one
++ * call to the next, so you must put adns_beforepoll in a loop, rather
++ * than assuming that the second call (with the buffer size requested
++ * by the first) will not return ERANGE.
++ *
++ * You may call _beforepoll with fds==0 and *nfds_io 0, in which case
++ * adns will fill in the number of fds that it might be interested in
++ * in *nfds_io, and always return 0.
++ *
++ * adns only ever sets POLLIN, POLLOUT and POLLPRI in its pollfd
++ * structs, and only ever looks at those bits.  POLLPRI is required to
++ * detect TCP Urgent Data, which should not be used by a DNS server,
++ * so that adns can know that the TCP stream is now useless.
++ *
++ * In any case, *timeout_io should be a timeout value as for poll(2),
++ * which adns will modify downwards as required.  If the caller does
++ * not plan to block then *timeout_io should be 0 on entry.
++ *
++ * adns_beforepoll will return 0 on success, and will not fail for any
++ * reason other than the fds buffer being too small (ERANGE).
++ *
++ * If *now is not 0 then this call will never actually do any I/O, or
++ * change the fds that adns is using or the timeouts it wants.  In any
++ * case it won't block.
++ */
++
++#define ADNS_POLLFDS_RECOMMENDED 2
++/* If you allocate an fds buf with at least RECOMMENDED entries then
++ * you are unlikely to need to enlarge it.  You are recommended to do
++ * so if it's convenient.  However, you must be prepared for adns to
++ * require more space than this.
++ */
++
++void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
++                  const struct timeval *now);
++/* Gives adns flow-of-control for a bit; intended for use after
++ * poll(2).  fds and nfds should be the results from poll().  pollfd
++ * structs mentioning fds not belonging to adns will be ignored.
++ */
++
++
  adns_status adns_rr_info(adns_rrtype type,
                         const char **rrtname_r, const char **fmtname_r,
                         int *len_r,
   */
  
  const char *adns_strerror(adns_status st);
 +const char *adns_errabbrev(adns_status st);
++/* Like strerror but for adns_status values.  adns_errabbrev returns
++ * the abbreviation of the error - eg, for adns_s_timeout it returns
++ * "timeout".  You MUST NOT call these functions with status values
++ * not returned by the same adns library.
++ */
  
  #endif
diff --cc src/adns.make
index 6d3979a5b7c5255bbf600c2e910fdc57c3428c31,6d3979a5b7c5255bbf600c2e910fdc57c3428c31..36b6820f74ee52cb747f38933e0d80861b12447a
@@@ -16,4 -16,4 +16,5 @@@
  #  along with this program; if not, write to the Free Software Foundation,
  #  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
  
--LIBOBJS=      types.o event.o query.o reply.o general.o setup.o transmit.o parse.o
++LIBOBJS=      types.o event.o query.o reply.o general.o setup.o transmit.o \
++              parse.o poll.o
diff --cc src/config.h.in
index 74267bfb20a024071958cc3ae4a8121499b428cb,a665e34a39b6daa63b129a300b5fde949bac4c5b..80590fb617267e9ddbeed822ec2edf036886d378
  /* Define if printf-format argument lists a la GCC are available.  */
  #undef HAVE_GNUC25_PRINTFFORMAT
  
++/* Define if you have the poll function.  */
++#undef HAVE_POLL
++
 +/* Define if you have the nsl library (-lnsl).  */
 +#undef HAVE_LIBNSL
 +
 +/* Define if you have the socket library (-lsocket).  */
 +#undef HAVE_LIBSOCKET
 +
  /* Use the definitions: */
  
++#ifdef HAVE_POLL
++#include <sys/poll.h>
++#else
++struct pollfd { int fd; short events; short revents; };
++#define POLLIN  1
++#define POLLPRI 2
++#define POLLOUT 4
++#endif
++
  /* GNU C attributes. */
  #ifndef FUNCATTR
  #ifdef HAVE_GNUC25_ATTRIB
diff --cc src/event.c
index 154a9b8f5c2dd3c62849b1e06ce8a16b4ecc7936,983e266949ef600677db2021f3a822571a206cfc..d54d8479c38151b36e914f298b99e99af1922491
  
  #include "internal.h"
  
--/* TCP connection management */
++/* TCP connection management. */
++
++void adns__tcp_closenext(adns_state ads) {
++  int serv;
++  
++  serv= ads->tcpserver;
++  close(ads->tcpsocket);
++  ads->tcpstate= server_disconnected;
++  ads->tcprecv.used= ads->tcpsend.used= 0;
++  ads->tcpserver= (serv+1)%ads->nservers;
++}
  
  void adns__tcp_broken(adns_state ads, const char *what, const char *why) {
    int serv;
@@@ -45,8 -40,8 +55,7 @@@
    assert(ads->tcpstate == server_connecting || ads->tcpstate == server_ok);
    serv= ads->tcpserver;
    adns__warn(ads,serv,0,"TCP connection lost: %s: %s",what,why);
--  close(ads->tcpsocket);
--  ads->tcpstate= server_disconnected;
++  adns__tcp_closenext(ads);
    
    for (qu= ads->timew.head; qu; qu= nqu) {
      nqu= qu->next;
@@@ -59,9 -54,9 +68,6 @@@
        adns__query_fail(qu,adns_s_allservfail);
      }
    }
--
--  ads->tcprecv.used= ads->tcpsend.used= 0;
--  ads->tcpserver= (serv+1)%ads->nservers;
  }
  
  static void tcp_connected(adns_state ads, struct timeval now) {
@@@ -114,9 -109,9 +120,20 @@@ void adns__tcp_tryconnect(adns_state ad
    }
  }
  
--/* `Interest' functions - find out which fd's we might be interested in,
-- * and when we want to be called back for a timeout.
-- */
++/* Timeout handling functions. */
++
++void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io,
++                           struct timeval *tv_buf) {
++  const struct timeval *now;
++  int r;
++
++  now= *now_io;
++  if (now) return;
++  r= gettimeofday(tv_buf,0); if (!r) { *now_io= tv_buf; return; }
++  adns__diag(ads,-1,0,"gettimeofday failed: %s",strerror(errno));
++  adns_globalsystemfailure(ads);
++  return;
++}
  
  static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf,
                        struct timeval maxto) {
@@@ -149,174 -144,228 +166,131 @@@ static void inter_maxtoabs(struct timev
    inter_maxto(tv_io,tvbuf,maxtime);
  }
  
--static void inter_addfd(int *maxfd, fd_set *fds, int fd) {
--  if (!maxfd || !fds) return;
--  if (fd>=*maxfd) *maxfd= fd+1;
--  FD_SET(fd,fds);
--}
--
--static void checktimeouts(adns_state ads, struct timeval now,
--                        struct timeval **tv_io, struct timeval *tvbuf) {
++void adns__timeouts(adns_state ads, int act,
++                  struct timeval **tv_io, struct timeval *tvbuf,
++                  struct timeval now) {
    adns_query qu, nqu;
--  
++
    for (qu= ads->timew.head; qu; qu= nqu) {
      nqu= qu->next;
--    if (timercmp(&now,&qu->timeout,>)) {
++    if (timercmp(&now,&qu->timeout,<=)) {
++      if (!tv_io) continue;
++      inter_maxtoabs(tv_io,tvbuf,now,qu->timeout);
++    } else {
++      if (!act) continue;
        LIST_UNLINK(ads->timew,qu);
        if (qu->state != query_udp) {
        adns__query_fail(qu,adns_s_timeout);
        } else {
        adns__query_udp(qu,now);
        }
--    } else {
--      inter_maxtoabs(tv_io,tvbuf,now,qu->timeout);
++      nqu= ads->timew.head;
      }
    }
-  
- void adns_interest(adns_state ads, int *maxfd,
-                  fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
-                  struct timeval **tv_io, struct timeval *tvbuf) {
-   struct timeval now;
-   struct timeval tvto_lr;
-   int r;
-   
- /*fprintf(stderr,"adns_interest\n");*/
 +}  
-   r= gettimeofday(&now,0);
-   if (r) {
-     adns__warn(ads,-1,0,"gettimeofday failed - will sleep for a bit: %s",
-              strerror(errno));
-     timerclear(&tvto_lr); timevaladd(&tvto_lr,LOCALRESOURCEMS);
-     inter_maxto(tv_io, tvbuf, tvto_lr);
-   } else {
-     checktimeouts(ads,now,tv_io,tvbuf);
-   }
-   
-   inter_addfd(maxfd,readfds,ads->udpsocket);
 +
 -static void checkfds(adns_state ads, int *maxfd,
 -                   fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
 -                   int avail, struct pollfd *buf, int *count_r) {
 -  int count;
++void adns_firsttimeout(adns_state ads,
++                     struct timeval **tv_io, struct timeval *tvbuf,
++                     struct timeval now) {
++  adns__timeouts(ads, 0, tv_io,tvbuf, now);
++}
++
++void adns_processtimeouts(adns_state ads, const struct timeval *now) {
++  struct timeval tv_buf;
++
++  adns__must_gettimeofday(ads,&now,&tv_buf); if (!now) return;
++  adns__timeouts(ads, 1, 0,0, *now);
+ }
 -  count= 0;
 -  inter_addfd(ads->udpsocket,maxfd,readfds);
 -  poll_addfd(ads->udpsocket,avail,buf,&count,POLLIN);
++/* fd handling functions.  These are the top-level of the real work of
++ * reception and often transmission.
++ */
++
++int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) {
++  /* Returns the number of entries filled in.  Always zeroes revents. */
++
++  assert(MAX_POLLFDS==2);
++  pollfds_buf[0].fd= ads->udpsocket;
++  pollfds_buf[0].events= POLLIN;
++  pollfds_buf[0].revents= 0;
  
    switch (ads->tcpstate) {
    case server_disconnected:
--    break;
++    return 1;
    case server_connecting:
-     inter_addfd(maxfd,writefds,ads->tcpsocket);
 -    inter_addfd(ads->tcpsocket,maxfd,writefds);
 -    poll_addfd(ads->tcpsocket,avail,buf,&count,POLLOUT);
++    pollfds_buf[1].events= POLLOUT;
      break;
    case server_ok:
-     inter_addfd(maxfd,readfds,ads->tcpsocket);
-     inter_addfd(maxfd,exceptfds,ads->tcpsocket);
-     if (ads->tcpsend.used) inter_addfd(maxfd,writefds,ads->tcpsocket);
 -    inter_addfd(ads->tcpsocket,maxfd,readfds);
 -    inter_addfd(ads->tcpsocket,maxfd,exceptfds);
 -    if (ads->tcpsend.used) inter_addfd(ads->tcpsocket,maxfd,writefds);
 -    poll_addfd(ads->tcpsocket,avail,buf,&count,
 -             ads->tcpsend.used ? POLLIN|POLLOUT : POLLIN);
++    pollfds_buf[1].events= ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI;
      break;
    default:
      abort();
    }
++  pollfds_buf[1].fd= ads->tcpsocket;
++  pollfds_buf[1].revents= 0;
++  return 2;
  }
  
 -struct pollfd *adns_pollfds(adns_state ads, struct pollfd *buf,
 -                          int *len_io, int *timeout_io) {
 -  struct timeval now, tvbuf, *tvp;
 -  int timeout;
 -  
 -  r= gettimeofday(&now,0);
 -  if (r) return 0;
 -
 -  timeout= *timeout_io;
 -  if (timeout < 0) {
 -    tvtop= 0;
 -  } else {
 -    tvbuf.tv_sec= now.tv_sec + (timeout/1000);
 -    tvbuf.tv_usec= now.tv_usec + (timeout%1000)*1000;
 -    if (tvbuf.tv_sec >= 1000000) {
 -      tvbuf.tv_sec += 1;
 -      tvbuf.tv_usec -= 1000000;
 -    }
 -    tvp= &tvbuf;
 -  }
 -  checktimouts(ads,now,&tvp,&tvbuf);
 -
 -  if (tvp) {
 -    assert(tvbuf.tv_sec<INT_MAX/1000);
 -    *timeout_io= (tvp->tv_sec - now.tv_sec)*1000 + (tvp->tv_usec - now.tv_usec)/1000;
 -  } else {
 -    *timeout_io= -1;
 -  }
 -
 -  avail= *len_io;
 -  if (avail == -1) {
 -    buf= ads->pollfdsbuf;
 -    avail= 2;
 -    if (!buf) {
 -      buf= ads->pollfdsbuf= malloc(sizeof(struct pollfd)*avail);
 -      if (!buf) return 0;
 -    }
 -  }
 -  checkfds(ads, 0,0,0,0, avail,buf,len_io);
 -  if (*len_io > avail) { errno= ENOSPC; return 0; }
 -
 -  return buf;
 -}
 - 
 -void adns_interest(adns_state ads, int *maxfd,
 -                 fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
 -                 struct timeval **tv_io, struct timeval *tvbuf) {
 -  struct timeval now;
 -  struct timeval tvto_lr;
 -  int r;
 -  
 -/*fprintf(stderr,"adns_interest\n");*/
 -
 -  r= gettimeofday(&now,0);
 -  if (r) {
 -    adns__warn(ads,-1,0,"gettimeofday failed - will sleep for a bit: %s",
 -             strerror(errno));
 -    timerclear(&tvto_lr); timevaladd(&tvto_lr,LOCALRESOURCEMS);
 -    inter_maxto(tv_io, tvbuf, tvto_lr);
 -  } else {
 -    checktimeouts(ads,now,tv_io,tvbuf);
 -  }
 -
 -  checkfds(ads, maxfd,readfds,writefds,exceptfds, 0,0,0);
 -}
 -
--/* Callback procedures - these do the real work of reception and timeout, etc. */
--
--static int callb_checkfd(int maxfd, const fd_set *fds, int fd) {
--  return maxfd<0 || !fds ? 1 :
--         fd<maxfd && FD_ISSET(fd,fds);
--}
--
--static int internal_callback(adns_state ads, int maxfd,
--                           const fd_set *readfds, const fd_set *writefds,
--                           const fd_set *exceptfds,
--                           struct timeval now) {
--  int skip, want, dgramlen, count, udpaddrlen, r, serv;
++int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
++  int skip, want, dgramlen, r, udpaddrlen, serv;
    byte udpbuf[DNS_MAXUDP];
    struct sockaddr_in udpaddr;
--
--  count= 0;
--
++  
    switch (ads->tcpstate) {
    case server_disconnected:
--    break;
    case server_connecting:
--    if (callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
--      count++;
--      assert(ads->tcprecv.used==0);
--      if (!adns__vbuf_ensure(&ads->tcprecv,1)) return -1;
--      if (ads->tcprecv.buf) {
--      r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
--      if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
--        tcp_connected(ads,now);
--      } else if (r>0) {
--        adns__tcp_broken(ads,"connect/read","sent data before first request");
--      } else if (errno!=EINTR) {
--        adns__tcp_broken(ads,"connect/read",strerror(errno));
--      }
--      }
--    }
      break;
    case server_ok:
--    count+= callb_checkfd(maxfd,readfds,ads->tcpsocket) +
--            callb_checkfd(maxfd,exceptfds,ads->tcpsocket) +
--      (ads->tcpsend.used && callb_checkfd(maxfd,writefds,ads->tcpsocket));
--    if (callb_checkfd(maxfd,readfds,ads->tcpsocket)) {
--      skip= 0;
--      for (;;) {
--      if (ads->tcprecv.used<skip+2) {
--        want= 2;
--      } else {
--        dgramlen= (ads->tcprecv.buf[skip]<<8) | ads->tcprecv.buf[skip+1];
--        if (ads->tcprecv.used<skip+2+dgramlen) {
--          want= 2+dgramlen;
--        } else {
--          adns__procdgram(ads,ads->tcprecv.buf+skip+2,dgramlen,ads->tcpserver,now);
--          skip+= 2+dgramlen; continue;
--        }
--      }
--      ads->tcprecv.used -= skip;
--      memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
--      skip= 0;
--      if (!adns__vbuf_ensure(&ads->tcprecv,want)) return -1;
--      assert(ads->tcprecv.used <= ads->tcprecv.avail);
--      if (ads->tcprecv.used == ads->tcprecv.avail) continue;
--      r= read(ads->tcpsocket,
--              ads->tcprecv.buf+ads->tcprecv.used,
--              ads->tcprecv.avail-ads->tcprecv.used);
--      if (r>0) {
--        ads->tcprecv.used+= r;
++    if (fd != ads->tcpsocket) break;
++    skip= 0;
++    for (;;) {
++      if (ads->tcprecv.used<skip+2) {
++      want= 2;
++      } else {
++      dgramlen= (ads->tcprecv.buf[skip]<<8) | ads->tcprecv.buf[skip+1];
++      if (ads->tcprecv.used<skip+2+dgramlen) {
++        want= 2+dgramlen;
        } else {
--        if (r<0) {
--          if (errno==EAGAIN || errno==EWOULDBLOCK || errno==ENOMEM) break;
--          if (errno==EINTR) continue;
--        }
--        adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
--        break;
++        adns__procdgram(ads,ads->tcprecv.buf+skip+2,dgramlen,ads->tcpserver,*now);
++        skip+= 2+dgramlen; continue;
        }
        }
--    } else if (callb_checkfd(maxfd,exceptfds,ads->tcpsocket)) {
--      adns__tcp_broken(ads,"select","exceptional condition detected");
--    } else if (ads->tcpsend.used && callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
-       adns__sigpipe_protect(ads);
--      r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
-       adns__sigpipe_unprotect(ads);
--      if (r<0) {
--      if (errno!=EAGAIN && errno!=EWOULDBLOCK && errno!=ENOMEM && errno!=EINTR) {
--        adns__tcp_broken(ads,"write",strerror(errno));
++      ads->tcprecv.used -= skip;
++      memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
++      skip= 0;
++      if (!adns__vbuf_ensure(&ads->tcprecv,want)) return ENOMEM;
++      assert(ads->tcprecv.used <= ads->tcprecv.avail);
++      if (ads->tcprecv.used == ads->tcprecv.avail) continue;
++      r= read(ads->tcpsocket,
++            ads->tcprecv.buf+ads->tcprecv.used,
++            ads->tcprecv.avail-ads->tcprecv.used);
++      if (r>0) {
++      ads->tcprecv.used+= r;
++      } else {
++      if (r) {
++        if (errno==EAGAIN || errno==EWOULDBLOCK) return 0;
++        if (errno==EINTR) continue;
++        if (errno_resources(errno)) return errno;
        }
--      } else if (r>0) {
--      ads->tcpsend.used -= r;
--      memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
++      adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
++      return 0;
        }
--    }
--    break;
++    } /* never reached */
    default:
      abort();
    }
--
--  if (callb_checkfd(maxfd,readfds,ads->udpsocket)) {
--    count++;
++  if (fd == ads->udpsocket) {
      for (;;) {
        udpaddrlen= sizeof(udpaddr);
 -      r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0,&udpaddr,&udpaddrlen);
 +      r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0,
 +                (struct sockaddr*)&udpaddr,&udpaddrlen);
        if (r<0) {
--      if (!(errno == EAGAIN || errno == EWOULDBLOCK ||
--            errno == EINTR || errno == ENOMEM || errno == ENOBUFS))
--        adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
--      break;
++      if (errno == EAGAIN || errno == EWOULDBLOCK) return 0;
++      if (errno == EINTR) continue;
++      if (errno_resources(errno)) return errno;
++      adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
++      return 0;
        }
        if (udpaddrlen != sizeof(udpaddr)) {
        adns__diag(ads,-1,0,"datagram received with wrong address length %d"
                   inet_ntoa(udpaddr.sin_addr));
        continue;
        }
--      adns__procdgram(ads,udpbuf,r,serv,now);
++      adns__procdgram(ads,udpbuf,r,serv,*now);
      }
    }
--  return count;
++  return 0;
  }
  
--int adns_callback(adns_state ads, int maxfd,
--                const fd_set *readfds, const fd_set *writefds,
--                const fd_set *exceptfds) {
--  struct timeval now;
++int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) {
+   int r;
++  
++  switch (ads->tcpstate) {
++  case server_disconnected:
++    break;
++  case server_connecting:
++    if (fd != ads->tcpsocket) break;
++    assert(ads->tcprecv.used==0);
++    for (;;) {
++      if (!adns__vbuf_ensure(&ads->tcprecv,1)) return ENOMEM;
++      r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
++      if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
++      tcp_connected(ads,*now);
++      return 0;
++      }
++      if (r>0) {
++      adns__tcp_broken(ads,"connect/read","sent data before first request");
++      return 0;
++      }
++      if (errno==EINTR) continue;
++      if (errno_resources(errno)) return errno;
++      adns__tcp_broken(ads,"connect/read",strerror(errno));
++      return 0;
++    } /* not reached */
++  case server_ok:
++    if (!(ads->tcpsend.used && fd == ads->tcpsocket)) break;
++    for (;;) {
++      adns__sigpipe_protect(ads);
++      r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
++      adns__sigpipe_unprotect(ads);
++      if (r<0) {
++      if (errno==EINTR) continue;
++      if (errno==EAGAIN || errno==EWOULDBLOCK) return 0;
++      if (errno_resources(errno)) return errno;
++      adns__tcp_broken(ads,"write",strerror(errno));
++      return 0;
++      } else if (r>0) {
++      ads->tcpsend.used -= r;
++      memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
++      }
++    } /* not reached */
++  default:
++    abort();
++  }
++  return 0;
++}
++  
++int adns_processexceptional(adns_state ads, int fd, const struct timeval *now) {
++  switch (ads->tcpstate) {
++  case server_disconnected:
++    break;
++  case server_connecting:
++  case server_ok:
++    if (fd != ads->tcpsocket) break;
++    adns__tcp_broken(ads,"poll/select","exceptional condition detected");
++    return 0;
++  default:
++    abort();
++  }
++  return 0;
++}
 -  r= gettimeofday(&now,0); if (r) return -1;
 -  checktimeouts(ads,now,0,0);
 -  return internal_callback(ads,maxfd,readfds,writefds,exceptfds,now);
++static void fd_event(adns_state ads, int fd,
++                   int revent, int pollflag,
++                   int maxfd, const fd_set *fds,
++                   int (*func)(adns_state, int fd, const struct timeval *now),
++                   struct timeval now, int *r_r) {
 +  int r;
++  
++  if (!(revent & pollflag)) return;
++  if (fds && !(fd<maxfd && FD_ISSET(fd,fds))) return;
++  r= func(ads,fd,&now);
++  if (r) {
++    if (r_r) {
++      *r_r= r;
++    } else {
++      adns__diag(ads,-1,0,"process fd failed after select: %s",strerror(errno));
++      adns_globalsystemfailure(ads);
++    }
++  }
++}
++
++void adns__fdevents(adns_state ads,
++                  const struct pollfd *pollfds, int npollfds,
++                  int maxfd, const fd_set *readfds,
++                  const fd_set *writefds, const fd_set *exceptfds,
++                  struct timeval now, int *r_r) {
++  int i, fd, revents;
++
++  for (i=0; i<npollfds; i++) {
++    fd= pollfds[i].fd;
++    if (fd >= maxfd) maxfd= fd+1;
++    revents= pollfds[i].revents;
++    fd_event(ads,fd, revents,POLLIN, maxfd,readfds, adns_processreadable,now,r_r);
++    fd_event(ads,fd, revents,POLLOUT, maxfd,writefds, adns_processwriteable,now,r_r);
++    fd_event(ads,fd, revents,POLLPRI, maxfd,exceptfds, adns_processexceptional,now,r_r);
++  }
++}
 +
-   r= gettimeofday(&now,0); if (r) return -1;
-   checktimeouts(ads,now,0,0);
-   return internal_callback(ads,maxfd,readfds,writefds,exceptfds,now);
++/* Wrappers for select(2). */
++
++void adns_beforeselect(adns_state ads, int *maxfd_io, fd_set *readfds_io,
++                     fd_set *writefds_io, fd_set *exceptfds_io,
++                     struct timeval **tv_mod, struct timeval *tv_tobuf,
++                     const struct timeval *now) {
++  struct timeval tv_nowbuf;
++  struct pollfd pollfds[MAX_POLLFDS];
++  int i, fd, maxfd, npollfds;
++  
++  if (tv_mod && (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) {
++    /* The caller is planning to sleep. */
++    adns__must_gettimeofday(ads,&now,&tv_nowbuf);
++    if (!now) return;
++    adns__timeouts(ads, 1, tv_mod,tv_tobuf, *now);
++  }
++
++  npollfds= adns__pollfds(ads,pollfds);
++  maxfd= *maxfd_io;
++  for (i=0; i<npollfds; i++) {
++    fd= pollfds[i].fd;
++    if (fd >= maxfd) maxfd= fd+1;
++    if (pollfds[i].events & POLLIN) FD_SET(fd,readfds_io);
++    if (pollfds[i].events & POLLOUT) FD_SET(fd,writefds_io);
++    if (pollfds[i].events & POLLPRI) FD_SET(fd,exceptfds_io);
++  }
++  *maxfd_io= maxfd;
++}
++
++void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
++                    const fd_set *writefds, const fd_set *exceptfds,
++                    const struct timeval *now) {
++  struct pollfd pollfds[MAX_POLLFDS];
++  int npollfds;
++
++  adns_processtimeouts(ads,now);
++
++  npollfds= adns__pollfds(ads,pollfds);
++  adns__fdevents(ads,
++               pollfds,npollfds,
++               maxfd,readfds,writefds,exceptfds,
++               *now, 0);
++}
++
++/* General helpful functions. */
++
++void adns_globalsystemfailure(adns_state ads) {
++  while (ads->timew.head) {
++    adns__query_fail(ads->timew.head, adns_s_systemfail);
++  }
++  
++  switch (ads->tcpstate) {
++  case server_connecting:
++  case server_ok:
++    adns__tcp_closenext(ads);
++    break;
++  case server_disconnected:
++    break;
++  default:
++    abort();
++  }
  }
  
--/* User-visible functions and their implementation. */
++int adns_processany(adns_state ads) {
++  int r;
++  struct timeval now;
++  struct pollfd pollfds[MAX_POLLFDS];
++  int npollfds;
++
++  r= gettimeofday(&now,0);
++  if (!r) adns_processtimeouts(ads,&now);
++
++  npollfds= adns__pollfds(ads,pollfds);
++  adns__fdevents(ads,
++               pollfds,npollfds,
++               0,0,0,0,
++               now,&r);
++  return r;
++}
  
  void adns__autosys(adns_state ads, struct timeval now) {
    if (ads->iflags & adns_if_noautosys) return;
--  adns_callback(ads,-1,0,0,0);
++  adns_processany(ads);
  }
  
  static int internal_check(adns_state ads,
@@@ -390,7 -439,7 +531,7 @@@ int adns_wait(adns_state ads
              adns_query *query_io,
              adns_answer **answer_r,
              void **context_r) {
--  int r, maxfd, rsel, rcb;
++  int r, maxfd, rsel;
    fd_set readfds, writefds, exceptfds;
    struct timeval tvbuf, *tvp;
    
      if (r != EWOULDBLOCK) return r;
      maxfd= 0; tvp= 0;
      FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
--    adns_interest(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf);
++    adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf,0);
      rsel= select(maxfd,&readfds,&writefds,&exceptfds,tvp);
      if (rsel==-1) {
--      if (errno == EINTR && !(ads->iflags & adns_if_eintr)) continue;
--      return errno;
++      if (errno == EINTR) {
++      if (ads->iflags & adns_if_eintr) return EINTR;
++      } else {
++      adns__diag(ads,-1,0,"select failed in wait: %s",strerror(errno));
++      adns_globalsystemfailure(ads);
++      }
++    } else {
++      assert(rsel >= 0);
++      adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,0);
      }
--    rcb= adns_callback(ads,maxfd,&readfds,&writefds,&exceptfds);
--    assert(rcb==rsel);
    }
  }
  
@@@ -417,7 -466,7 +563,8 @@@ int adns_check(adns_state ads
    struct timeval now;
    int r;
    
--  r= gettimeofday(&now,0); if (r) return errno;
--  adns__autosys(ads,now);
++  r= gettimeofday(&now,0);
++  if (!r) adns__autosys(ads,now);
++  
    return internal_check(ads,query_io,answer_r,context_r);
  }
diff --cc src/internal.h
index b813215f9071043fc04730bca6d5fbbd3e427708,94b8b20a8efe01be83584006d2ac19dbe8564cd3..dadd854a88e42c05b75a88471bc446f90ba21988
@@@ -31,7 -31,6 +31,8 @@@ typedef unsigned char byte
  #include <stdarg.h>
  #include <assert.h>
  #include <unistd.h>
 +#include <signal.h>
++#include <errno.h>
  
  #include <sys/time.h>
  
@@@ -44,7 -43,7 +45,6 @@@
  #define UDPMAXRETRIES 15
  #define UDPRETRYMS 2000
  #define TCPMS 30000
--#define LOCALRESOURCEMS 20
  #define MAXTTLBELIEVE (7*86400) /* any TTL > 7 days is capped */
  
  #define DNS_PORT 53
@@@ -56,6 -54,6 +56,8 @@@
  
  #define DNS_INADDR_ARPA "in-addr", "arpa"
  
++#define MAX_POLLFDS  ADNS_POLLFDS_RECOMMENDED
++
  typedef enum {
    rcode_noerror,
    rcode_formaterror,
@@@ -264,11 -246,10 +266,12 @@@ struct adns__state 
    struct { adns_query head, tail; } timew, childw, output;
    int nextid, udpsocket, tcpsocket;
    vbuf tcpsend, tcprecv;
 -  int nservers, nsortlist, tcpserver;
 +  int nservers, nsortlist, nsearchlist, searchndots, tcpserver;
    enum adns__tcpstate { server_disconnected, server_connecting, server_ok } tcpstate;
    struct timeval tcptimeout;
 -  struct pollfd *pollfdsbuf; /* fixme: init and cleanup */
 +  struct sigaction stdsigpipe;
 +  sigset_t stdsigmask;
++  struct pollfd pollfds_buf[MAX_POLLFDS];
    struct server {
      struct in_addr addr;
    } servers[MAXSERVERS];
@@@ -598,11 -542,11 +601,24 @@@ int vbuf__append_quoted1035(vbuf *vb, c
  /* From event.c: */
  
  void adns__tcp_broken(adns_state ads, const char *what, const char *why);
++void adns__tcp_closenext(adns_state ads);
  void adns__tcp_tryconnect(adns_state ads, struct timeval now);
  
  void adns__autosys(adns_state ads, struct timeval now);
  /* Make all the system calls we want to if the application wants us to. */
  
++void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io,
++                           struct timeval *tv_buf);
++void adns__timeouts(adns_state ads, int act,
++                  struct timeval **tv_io, struct timeval *tvbuf,
++                  struct timeval now);
++int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]);
++void adns__fdevents(adns_state ads,
++                  const struct pollfd *pollfds, int npollfds,
++                  int maxfd, const fd_set *readfds,
++                  const fd_set *writefds, const fd_set *exceptfds,
++                  struct timeval now, int *r_r);
++
  /* Useful static inline functions: */
  
  static inline void timevaladd(struct timeval *tv_io, long ms) {
  static inline int ctype_whitespace(int c) { return c==' ' || c=='\n' || c=='\t'; }
  static inline int ctype_digit(int c) { return c>='0' && c<='9'; }
  static inline int ctype_alpha(int c) {
 -  return (c >= 'a' && c <= 'z') || (c >= 'A' || c <= 'Z');
 +  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
  }
  
++static inline int errno_resources(int e) { return e==ENOMEM || e==ENOBUFS; }
++
  /* Useful macros */
  
  #define MEM_ROUND(sz) \
diff --cc src/poll.c
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..03f153a0b3849612a0b427ee7d5b1063c794f3c7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++/*
++ * poll.c
++ * - wrappers for poll(2)
++ */
++/*
++ *  This file is part of adns, which is Copyright (C) 1997-1999 Ian Jackson
++ *  
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation; either version 2, or (at your option)
++ *  any later version.
++ *  
++ *  This program 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 General Public License for more details.
++ *  
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program; if not, write to the Free Software Foundation,
++ *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
++ */
++
++#include <limits.h>
++
++#include "internal.h"
++
++#ifdef HAVE_POLL
++
++int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io,
++                  const struct timeval *now) {
++  struct timeval tv_nowbuf, tv_tobuf, *tv_to;
++  int space, found, timeout_ms;
++  struct pollfd fds_tmp[MAX_POLLFDS];
++
++  adns__must_gettimeofday(ads,&now,&tv_nowbuf);
++  if (!now) { *nfds_io= 0; return 0; }
++
++  timeout_ms= *timeout_io;
++  if (timeout_ms == -1) {
++    tv_to= 0;
++  } else {
++    tv_tobuf.tv_sec= timeout_ms / 1000;
++    tv_tobuf.tv_usec= (timeout_ms % 1000)*1000;
++    tv_to= &tv_tobuf;
++  }
++
++  adns__timeouts(ads, 1, &tv_to,&tv_tobuf, *now);
++
++  if (tv_to) {
++    assert(tv_to == &tv_tobuf);
++    timeout_ms= (tv_tobuf.tv_usec+999)/1000;
++    assert(tv_tobuf.tv_sec < (INT_MAX-timeout_ms)/1000);
++    timeout_ms += tv_tobuf.tv_sec*1000;
++  } else {
++    timeout_ms= -1;
++  }
++  *timeout_io= timeout_ms;
++  
++  space= *nfds_io;
++  if (space >= MAX_POLLFDS) {
++    found= adns__pollfds(ads,fds);
++    *nfds_io= found;
++  } else {
++    found= adns__pollfds(ads,fds_tmp);
++    *nfds_io= found;
++    if (found < space) return space ? ERANGE : 0;
++    memcpy(fds,fds_tmp,sizeof(struct pollfd)*found);
++  }
++  return 0;
++}
++
++void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
++                  const struct timeval *now) {
++  struct timeval tv_buf;
++
++  adns__must_gettimeofday(ads,&now,&tv_buf);
++  if (!now) return;
++
++  adns__timeouts(ads,1, 0,0, *now);
++  adns__fdevents(ads, fds,nfds, 0,0,0,0, *now,0);
++}
++
++#endif