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: wip.base.getaddrinfo~330 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=74c948317bd4785fbfa8b6047b8da2e1705b1863;p=adns.git New poll(2) stuff etc. Does not work yet, but compiles. --- diff --git a/acconfig.h b/acconfig.h index 73a7fe3..16672a6 100644 --- a/acconfig.h +++ b/acconfig.h @@ -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 --git a/changelog b/changelog index d83d3c2..207c322 100644 --- a/changelog +++ b/changelog @@ -1,8 +1,14 @@ 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. diff --git a/configure b/configure index 4bf677b..5df8065 100755 --- a/configure +++ b/configure @@ -719,14 +719,69 @@ else 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 <&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 @@ -768,7 +823,7 @@ 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 @@ -776,7 +831,7 @@ 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 @@ -823,12 +878,12 @@ 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 <&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 @@ -870,7 +925,7 @@ 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 @@ -878,7 +933,7 @@ 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 @@ -926,12 +981,12 @@ 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 <&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 @@ -973,7 +1028,7 @@ 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 @@ -981,7 +1036,7 @@ 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 @@ -1026,20 +1081,20 @@ fi echo $ac_n "checking __attribute__((,,))""... $ac_c" 1>&6 -echo "configure:1030: 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:1098: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* adns_cv_c_attribute_supported=yes else @@ -1061,20 +1116,20 @@ EOF echo $ac_n "checking __attribute__((noreturn))""... $ac_c" 1>&6 -echo "configure:1065: 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:1133: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* adns_cv_c_attribute_noreturn=yes else @@ -1101,20 +1156,20 @@ EOF echo $ac_n "checking __attribute__((const))""... $ac_c" 1>&6 -echo "configure:1105: 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:1173: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* adns_cv_c_attribute_const=yes else @@ -1141,20 +1196,20 @@ EOF echo $ac_n "checking __attribute__((format...))""... $ac_c" 1>&6 -echo "configure:1145: 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:1213: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* adns_cv_attribute_format=yes else diff --git a/configure.in b/configure.in index 0267a37..6e342c0 100644 --- a/configure.in +++ b/configure.in @@ -21,6 +21,7 @@ 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) diff --git a/src/adns.h b/src/adns.h index 1e7544c..14bd971 100644 --- a/src/adns.h +++ b/src/adns.h @@ -19,7 +19,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * $Id: adns.h,v 1.49 1999/06/17 00:54:05 ian Exp $ + * $Id: adns.h,v 1.50 1999/07/05 01:51:04 ian Exp $ */ #ifndef ADNS_H_INCLUDED @@ -111,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 +121,8 @@ typedef enum { 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 +130,7 @@ typedef enum { adns_s_rcodenotimplemented, adns_s_rcoderefused, adns_s_rcodeunknown, - + adns_s_max_tempfail= 99, /* remote configuration errors */ @@ -228,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 +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 +290,37 @@ int adns_wait(adns_state ads, void adns_cancel(adns_query query); -void adns_finish(adns_state); +/* 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. + */ + +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. +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. + * + * 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 +331,195 @@ void adns_interest(adns_state, int *maxfd_io, fd_set *readfds_io, * 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, @@ -357,5 +550,10 @@ adns_status adns_rr_info(adns_rrtype type, 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 --git a/src/adns.make b/src/adns.make index 6d3979a..36b6820 100644 --- a/src/adns.make +++ b/src/adns.make @@ -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 --git a/src/config.h.in b/src/config.h.in index 74267bf..80590fb 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -12,6 +12,9 @@ /* 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 @@ -20,6 +23,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 --git a/src/event.c b/src/event.c index 154a9b8..d54d847 100644 --- a/src/event.c +++ b/src/event.c @@ -36,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 +55,7 @@ void adns__tcp_broken(adns_state ads, const char *what, const char *why) { 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 +68,6 @@ void adns__tcp_broken(adns_state ads, const char *what, const char *why) { 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 +120,20 @@ void adns__tcp_tryconnect(adns_state ads, struct timeval now) { } } -/* `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 +166,131 @@ static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf, 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); +} + +/* 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); + 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); + 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; } -/* 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, (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 +316,195 @@ static int internal_callback(adns_state ads, int maxfd, 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; +} + +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 +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 +540,19 @@ int adns_wait(adns_state ads, 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 +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 --git a/src/internal.h b/src/internal.h index b813215..dadd854 100644 --- a/src/internal.h +++ b/src/internal.h @@ -32,6 +32,7 @@ typedef unsigned char byte; #include #include #include +#include #include @@ -44,7 +45,6 @@ typedef unsigned char byte; #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 +56,8 @@ typedef unsigned char byte; #define DNS_INADDR_ARPA "in-addr", "arpa" +#define MAX_POLLFDS ADNS_POLLFDS_RECOMMENDED + typedef enum { rcode_noerror, rcode_formaterror, @@ -269,6 +271,7 @@ struct adns__state { struct timeval tcptimeout; struct sigaction stdsigpipe; sigset_t stdsigmask; + struct pollfd pollfds_buf[MAX_POLLFDS]; struct server { struct in_addr addr; } servers[MAXSERVERS]; @@ -598,11 +601,24 @@ int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len); /* 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) { @@ -621,6 +637,8 @@ static inline int ctype_alpha(int c) { 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 --git a/src/poll.c b/src/poll.c new file mode 100644 index 0000000..03f153a --- /dev/null +++ b/src/poll.c @@ -0,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