From: ian Date: Mon, 5 Jul 1999 01:50:58 +0000 (+0000) Subject: New poll(2) stuff etc. Does not work yet, but compiles. X-Git-Tag: rel-adns-0-3~11 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=adns.git;a=commitdiff_plain;h=620c146da30a20c2d9a6c572fdd0540558039d0e New poll(2) stuff etc. Does not work yet, but compiles. --- 620c146da30a20c2d9a6c572fdd0540558039d0e diff --cc acconfig.h index 73a7fe3,73a7fe3..16672a6 --- a/acconfig.h +++ b/acconfig.h @@@ -36,6 -36,6 +36,15 @@@ /* Use the definitions: */ ++#ifdef HAVE_POLL ++#include ++#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 d83d3c2,e1fa705..207c322 --- a/changelog +++ 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 and in files with . + * 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 in a few more files. + * Include and 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 Thu, 20 May 1999 00:27:32 +0100 + adns (0.1) experimental; urgency=low * Initial public alpha release. diff --cc configure index 4bf677b,5cc73d5..5df8065 --- a/configure +++ b/configure @@@ -719,327 -719,22 +719,382 @@@ els fi ++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 < ++/* 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 <&6 ++fi ++done ++ + + echo $ac_n "checking for socket""... $ac_c" 1>&6 - echo "configure:725: checking for socket" >&5 ++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 < +/* 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 - if { (eval echo configure:753: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then ++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 - echo "configure:772: checking for socket in -lsocket" >&5 ++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 <&5; (eval $ac_link) 2>&5; } && test -s conftest; then ++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 <&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 - echo "configure:827: checking for inet_ntoa" >&5 ++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 < +/* 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 - if { (eval echo configure:855: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then ++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 - echo "configure:874: checking for inet_ntoa in -lnsl" >&5 ++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 <&5; (eval $ac_link) 2>&5; } && test -s conftest; then ++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 <&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 - echo "configure:930: checking for inet_aton" >&5 ++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 < +/* 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 - if { (eval echo configure:958: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then ++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 - echo "configure:977: checking for inet_aton in -lresolv" >&5 ++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 <&5; (eval $ac_link) 2>&5; } && test -s conftest; then ++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 <&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 @@@ -1061,20 -756,20 +1116,20 @@@ EO 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 <&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 @@@ -1101,20 -796,20 +1156,20 @@@ EO 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 <&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 @@@ -1141,20 -836,20 +1196,20 @@@ EO 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 <&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 0267a37,8d40cc1..6e342c0 --- a/configure.in +++ b/configure.in @@@ -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 88b51f8,d3ab7ed..ce92c8b --- a/src/adns.h +++ b/src/adns.h @@@ -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, @@@ -118,6 -116,6 +121,8 @@@ 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, @@@ -125,7 -123,7 +130,7 @@@ 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 @@@ -315,18 -343,18 +331,195 @@@ * 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 ++ * , 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, @@@ -356,6 -384,5 +549,11 @@@ */ 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 6d3979a,6d3979a..36b6820 --- a/src/adns.make +++ b/src/adns.make @@@ -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 74267bf,a665e34..80590fb --- a/src/config.h.in +++ b/src/config.h.in @@@ -12,14 -12,8 +12,26 @@@ /* 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 ++#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 154a9b8,983e266..d54d847 --- a/src/event.c +++ b/src/event.c @@@ -36,7 -31,7 +36,17 @@@ #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); ++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); + } + -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; ++/* 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); + - count= 0; - inter_addfd(ads->udpsocket,maxfd,readfds); - poll_addfd(ads->udpsocket,avail,buf,&count,POLLIN); ++ 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_sectv_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 : -- fdtcpstate) { 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.usedtcprecv.buf[skip]<<8) | ads->tcprecv.buf[skip+1]; -- if (ads->tcprecv.usedtcprecv.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.usedtcprecv.buf[skip]<<8) | ads->tcprecv.buf[skip+1]; ++ if (ads->tcprecv.usedtcprecv.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" @@@ -342,28 -391,28 +316,195 @@@ 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) 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= 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; @@@ -399,14 -448,14 +540,19 @@@ 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 b813215,94b8b20..dadd854 --- a/src/internal.h +++ b/src/internal.h @@@ -31,7 -31,6 +31,8 @@@ typedef unsigned char byte #include #include #include +#include ++#include #include @@@ -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) { @@@ -618,9 -562,9 +634,11 @@@ 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 0000000,0000000..03f153a new file mode 100644 --- /dev/null +++ b/src/poll.c @@@ -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 ++ ++#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