/* Use the definitions: */
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#else
+struct pollfd { int fd; short events; short revents; };
+#define POLLIN 1
+#define POLLPRI 2
+#define POLLOUT 4
+#endif
+
/* GNU C attributes. */
#ifndef FUNCATTR
#ifdef HAVE_GNUC25_ATTRIB
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.
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 <<EOF
+#line 731 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:754: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
echo $ac_n "checking for socket""... $ac_c" 1>&6
-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 <<EOF
-#line 730 "configure"
+#line 785 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char socket(); below. */
; 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 "$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
ac_save_LIBS="$LIBS"
LIBS="-lsocket $LIBS"
cat > conftest.$ac_ext <<EOF
-#line 780 "configure"
+#line 835 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
socket()
; return 0; }
EOF
-if { (eval echo configure:791: \"$ac_link\") 1>&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 $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 <<EOF
-#line 832 "configure"
+#line 887 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char inet_ntoa(); below. */
; 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 "$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
ac_save_LIBS="$LIBS"
LIBS="-lnsl $LIBS"
cat > conftest.$ac_ext <<EOF
-#line 882 "configure"
+#line 937 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
inet_ntoa()
; return 0; }
EOF
-if { (eval echo configure:893: \"$ac_link\") 1>&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 $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 <<EOF
-#line 935 "configure"
+#line 990 "configure"
#include "confdefs.h"
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char inet_aton(); below. */
; 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 "$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
ac_save_LIBS="$LIBS"
LIBS="-lresolv $LIBS"
cat > conftest.$ac_ext <<EOF
-#line 985 "configure"
+#line 1040 "configure"
#include "confdefs.h"
/* Override any gcc2 internal prototype to avoid an error. */
/* We use char because int might match the return type of a gcc2
inet_aton()
; return 0; }
EOF
-if { (eval echo configure:996: \"$ac_link\") 1>&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 $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 <<EOF
-#line 1036 "configure"
+#line 1091 "configure"
#include "confdefs.h"
int main() {
extern int testfunction(int x) __attribute__((,,))
; return 0; }
EOF
-if { (eval echo configure:1043: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1098: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
adns_cv_c_attribute_supported=yes
else
echo $ac_n "checking __attribute__((noreturn))""... $ac_c" 1>&6
-echo "configure:1065: checking __attribute__((noreturn))" >&5
+echo "configure:1120: checking __attribute__((noreturn))" >&5
if eval "test \"`echo '$''{'adns_cv_c_attribute_noreturn'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
-#line 1071 "configure"
+#line 1126 "configure"
#include "confdefs.h"
int main() {
extern int testfunction(int x) __attribute__((noreturn))
; return 0; }
EOF
-if { (eval echo configure:1078: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1133: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
adns_cv_c_attribute_noreturn=yes
else
echo $ac_n "checking __attribute__((const))""... $ac_c" 1>&6
-echo "configure:1105: checking __attribute__((const))" >&5
+echo "configure:1160: checking __attribute__((const))" >&5
if eval "test \"`echo '$''{'adns_cv_c_attribute_const'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
-#line 1111 "configure"
+#line 1166 "configure"
#include "confdefs.h"
int main() {
extern int testfunction(int x) __attribute__((const))
; return 0; }
EOF
-if { (eval echo configure:1118: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1173: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
adns_cv_c_attribute_const=yes
else
echo $ac_n "checking __attribute__((format...))""... $ac_c" 1>&6
-echo "configure:1145: checking __attribute__((format...))" >&5
+echo "configure:1200: checking __attribute__((format...))" >&5
if eval "test \"`echo '$''{'adns_cv_attribute_format'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
-#line 1151 "configure"
+#line 1206 "configure"
#include "confdefs.h"
int main() {
extern int testfunction(char *y, ...) __attribute__((format(printf,1,2)))
; return 0; }
EOF
-if { (eval echo configure:1158: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1213: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
adns_cv_attribute_format=yes
else
AC_PROG_CC
AC_PROG_RANLIB
+AC_CHECK_FUNCS(poll)
ADNS_C_GETFUNC(socket,socket)
ADNS_C_GETFUNC(inet_ntoa,nsl)
/* locally induced errors */
adns_s_nomemory,
adns_s_unknownrrtype,
+ adns_s_systemfail,
+
+ adns_s_max_localfail= 29,
/* remotely induced errors, detected locally */
adns_s_timeout,
adns_s_norecurse,
adns_s_invalidresponse,
adns_s_unknownformat,
+
+ adns_s_max_remotefail= 59,
/* remotely induced errors, reported by remote server to us */
adns_s_rcodeservfail,
adns_s_rcodenotimplemented,
adns_s_rcoderefused,
adns_s_rcodeunknown,
-
+
adns_s_max_tempfail= 99,
/* remote configuration errors */
* 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,
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,
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
* adns_wait 3
* ....
* adns_finish
+ */
+
+/*
+ * Entrypoints for generic asynch io:
+ * (these entrypoints are not very useful except in combination with *
+ * some of the other I/O model calls which can tell you which fds to
+ * be interested in):
+ *
+ * Note that any adns call may cause adns to open and close fds, so
+ * you must call beforeselect or beforepoll again just before
+ * blocking, or you may not have an up-to-date list of it's fds.
+ */
+
+int adns_processany(adns_state ads);
+/* Gives adns flow-of-control for a bit. This will never block, and
+ * can be used with any threading/asynch-io model. If some error
+ * occurred which might cause an event loop to spin then the errno
+ * value is returned.
+ */
+
+int adns_processreadable(adns_state ads, int fd, const struct timeval *now);
+int adns_processwriteable(adns_state ads, int fd, const struct timeval *now);
+int adns_processexceptional(adns_state ads, int fd, const struct timeval *now);
+/* Gives adns flow-of-control so that it can process incoming data
+ * from, or send outgoing data via, fd. Very like _processany. If it
+ * returns zero then fd will no longer be readable or writeable
+ * (unless of course more data has arrived since). adns will _only_
+ * that fd and only in the manner specified, regardless of whether
+ * adns_if_noautosys was specified.
+ *
+ * adns_processexceptional should be called when select(2) reports an
+ * exceptional condition, or poll(2) reports POLLPRI.
+ *
+ * It is fine to call _processreabable or _processwriteable when the
+ * fd is not ready, or with an fd that doesn't belong to adns; it will
+ * then just return 0.
+ *
+ * If some error occurred which might prevent an event loop to spin
+ * then the errno value is returned.
+ */
+
+void adns_processtimeouts(adns_state ads, const struct timeval *now);
+/* Gives adns flow-of-control so that it can process any timeouts
+ * which might have happened. Very like _processreadable/writeable.
+ *
+ * now may be 0; if it isn't, *now must be the current time, recently
+ * obtained from gettimeofday.
+ */
+
+void adns_firsttimeout(adns_state ads,
+ struct timeval **tv_mod, struct timeval *tv_buf,
+ struct timeval now);
+/* Asks adns when it would first like the opportunity to time
+ * something out. now must be the current time, from gettimeofday.
+ *
+ * If tv_mod points to 0 then tv_buf must be non-null, and
+ * _firsttimeout will fill in *tv_buf with the time until the first
+ * timeout, and make *tv_mod point to tv_buf. If adns doesn't have
+ * anything that might need timing out it will leave *tv_mod as 0.
+ *
+ * If *tv_mod is not 0 then tv_buf is not used. adns will update
+ * *tv_mod if it has any earlier timeout, and leave it alone if it
+ * doesn't.
+ *
+ * This call will not actually do any I/O, or change the fds that adns
+ * is using. It always succeeds and never blocks.
+ */
+
+void adns_globalsystemfailure(adns_state ads);
+/* If serious problem(s) happen which globally affect your ability to
+ * interact properly with adns, or adns's ability to function
+ * properly, you or adns can call this function.
+ *
+ * All currently outstanding queries will be made to fail with
+ * adns_s_systemfail, and adns will close any stream sockets it has
+ * open.
+ *
+ * This is used by adns, for example, if gettimeofday() fails.
+ * Without this the program's event loop might start to spin !
+ *
+ * This call will never block.
+ */
+
+/*
+ * Entrypoints for select-loop based asynch io:
+ */
+
+void adns_beforeselect(adns_state ads, int *maxfd, fd_set *readfds,
+ fd_set *writefds, fd_set *exceptfds,
+ struct timeval **tv_mod, struct timeval *tv_buf,
+ const struct timeval *now);
+/* Find out file descriptors adns is interested in, and when it would
+ * like the opportunity to time something out. If you do not plan to
+ * block then tv_mod may be 0. Otherwise, tv_mod and tv_buf are as
+ * for adns_firsttimeout. readfds, writefds, exceptfds and maxfd_io may
+ * not be 0.
+ *
+ * If *now is not 0 then this will never actually do any I/O, or
+ * change the fds that adns is using or the timeouts it wants. In any
+ * case it won't block.
+ */
+
+void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
+ const fd_set *writefds, const fd_set *exceptfds,
+ const struct timeval *now);
+/* Gives adns flow-of-control for a bit; intended for use after
+ * select. This is just a fancy way of calling adns_processreadable/
+ * writeable/timeouts as appropriate, as if select had returned the
+ * data being passed. Always succeeds.
+ */
+
+/*
+ * Example calling sequence:
*
* adns_init _noautosys
* loop {
- * adns_interest
+ * adns_beforeselect
* select
- * adns_callback
+ * adns_afterselect
* ...
* adns_submit / adns_check
* ...
* }
*/
+/*
+ * Entrypoints for poll-loop based asynch io:
+ */
+
+struct pollfd;
+/* In case your system doesn't have it or you forgot to include
+ * <sys/poll.h>, to stop the following declarations from causing
+ * problems. If your system doesn't have poll then the following
+ * entrypoints will not be defined in libadns. Sorry !
+ */
+
+int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io,
+ const struct timeval *now);
+/* Finds out which fd's adns is interested in, and when it would like
+ * to be able to time things out. This is in a form suitable for use
+ * with poll(2).
+ *
+ * On entry, usually fds should point to at least *nfds_io structs.
+ * adns will fill up to that many structs will information for poll,
+ * and record in *nfds_io how many structs it filled. If it wants to
+ * listen for more structs then *nfds_io will be set to the number
+ * required and _beforepoll will return ERANGE.
+ *
+ * NOTE that if now is 0, adns may acquire additional fds from one
+ * call to the next, so you must put adns_beforepoll in a loop, rather
+ * than assuming that the second call (with the buffer size requested
+ * by the first) will not return ERANGE.
+ *
+ * You may call _beforepoll with fds==0 and *nfds_io 0, in which case
+ * adns will fill in the number of fds that it might be interested in
+ * in *nfds_io, and always return 0.
+ *
+ * adns only ever sets POLLIN, POLLOUT and POLLPRI in its pollfd
+ * structs, and only ever looks at those bits. POLLPRI is required to
+ * detect TCP Urgent Data, which should not be used by a DNS server,
+ * so that adns can know that the TCP stream is now useless.
+ *
+ * In any case, *timeout_io should be a timeout value as for poll(2),
+ * which adns will modify downwards as required. If the caller does
+ * not plan to block then *timeout_io should be 0 on entry.
+ *
+ * adns_beforepoll will return 0 on success, and will not fail for any
+ * reason other than the fds buffer being too small (ERANGE).
+ *
+ * If *now is not 0 then this call will never actually do any I/O, or
+ * change the fds that adns is using or the timeouts it wants. In any
+ * case it won't block.
+ */
+
+#define ADNS_POLLFDS_RECOMMENDED 2
+/* If you allocate an fds buf with at least RECOMMENDED entries then
+ * you are unlikely to need to enlarge it. You are recommended to do
+ * so if it's convenient. However, you must be prepared for adns to
+ * require more space than this.
+ */
+
+void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
+ const struct timeval *now);
+/* Gives adns flow-of-control for a bit; intended for use after
+ * poll(2). fds and nfds should be the results from poll(). pollfd
+ * structs mentioning fds not belonging to adns will be ignored.
+ */
+
+
adns_status adns_rr_info(adns_rrtype type,
const char **rrtname_r, const char **fmtname_r,
int *len_r,
const char *adns_strerror(adns_status st);
const char *adns_errabbrev(adns_status st);
+/* Like strerror but for adns_status values. adns_errabbrev returns
+ * the abbreviation of the error - eg, for adns_s_timeout it returns
+ * "timeout". You MUST NOT call these functions with status values
+ * not returned by the same adns library.
+ */
#endif
# 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
/* 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
/* Use the definitions: */
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#else
+struct pollfd { int fd; short events; short revents; };
+#define POLLIN 1
+#define POLLPRI 2
+#define POLLOUT 4
+#endif
+
/* GNU C attributes. */
#ifndef FUNCATTR
#ifdef HAVE_GNUC25_ATTRIB
#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;
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;
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) {
}
}
-/* `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) {
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 :
- fd<maxfd && FD_ISSET(fd,fds);
-}
-
-static int internal_callback(adns_state ads, int maxfd,
- const fd_set *readfds, const fd_set *writefds,
- const fd_set *exceptfds,
- struct timeval now) {
- int skip, want, dgramlen, count, udpaddrlen, r, serv;
+int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
+ int skip, want, dgramlen, r, udpaddrlen, serv;
byte udpbuf[DNS_MAXUDP];
struct sockaddr_in udpaddr;
-
- count= 0;
-
+
switch (ads->tcpstate) {
case server_disconnected:
- break;
case server_connecting:
- if (callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
- count++;
- assert(ads->tcprecv.used==0);
- if (!adns__vbuf_ensure(&ads->tcprecv,1)) return -1;
- if (ads->tcprecv.buf) {
- r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
- if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
- tcp_connected(ads,now);
- } else if (r>0) {
- adns__tcp_broken(ads,"connect/read","sent data before first request");
- } else if (errno!=EINTR) {
- adns__tcp_broken(ads,"connect/read",strerror(errno));
- }
- }
- }
break;
case server_ok:
- count+= callb_checkfd(maxfd,readfds,ads->tcpsocket) +
- callb_checkfd(maxfd,exceptfds,ads->tcpsocket) +
- (ads->tcpsend.used && callb_checkfd(maxfd,writefds,ads->tcpsocket));
- if (callb_checkfd(maxfd,readfds,ads->tcpsocket)) {
- skip= 0;
- for (;;) {
- if (ads->tcprecv.used<skip+2) {
- want= 2;
- } else {
- dgramlen= (ads->tcprecv.buf[skip]<<8) | ads->tcprecv.buf[skip+1];
- if (ads->tcprecv.used<skip+2+dgramlen) {
- want= 2+dgramlen;
- } else {
- adns__procdgram(ads,ads->tcprecv.buf+skip+2,dgramlen,ads->tcpserver,now);
- skip+= 2+dgramlen; continue;
- }
- }
- ads->tcprecv.used -= skip;
- memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
- skip= 0;
- if (!adns__vbuf_ensure(&ads->tcprecv,want)) return -1;
- assert(ads->tcprecv.used <= ads->tcprecv.avail);
- if (ads->tcprecv.used == ads->tcprecv.avail) continue;
- r= read(ads->tcpsocket,
- ads->tcprecv.buf+ads->tcprecv.used,
- ads->tcprecv.avail-ads->tcprecv.used);
- if (r>0) {
- ads->tcprecv.used+= r;
+ if (fd != ads->tcpsocket) break;
+ skip= 0;
+ for (;;) {
+ if (ads->tcprecv.used<skip+2) {
+ want= 2;
+ } else {
+ dgramlen= (ads->tcprecv.buf[skip]<<8) | ads->tcprecv.buf[skip+1];
+ if (ads->tcprecv.used<skip+2+dgramlen) {
+ want= 2+dgramlen;
} else {
- if (r<0) {
- if (errno==EAGAIN || errno==EWOULDBLOCK || errno==ENOMEM) break;
- if (errno==EINTR) continue;
- }
- adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
- break;
+ adns__procdgram(ads,ads->tcprecv.buf+skip+2,dgramlen,ads->tcpserver,*now);
+ skip+= 2+dgramlen; continue;
}
}
- } else if (callb_checkfd(maxfd,exceptfds,ads->tcpsocket)) {
- adns__tcp_broken(ads,"select","exceptional condition detected");
- } else if (ads->tcpsend.used && callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
- adns__sigpipe_protect(ads);
- r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
- adns__sigpipe_unprotect(ads);
- if (r<0) {
- if (errno!=EAGAIN && errno!=EWOULDBLOCK && errno!=ENOMEM && errno!=EINTR) {
- adns__tcp_broken(ads,"write",strerror(errno));
+ ads->tcprecv.used -= skip;
+ memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
+ skip= 0;
+ if (!adns__vbuf_ensure(&ads->tcprecv,want)) return ENOMEM;
+ assert(ads->tcprecv.used <= ads->tcprecv.avail);
+ if (ads->tcprecv.used == ads->tcprecv.avail) continue;
+ r= read(ads->tcpsocket,
+ ads->tcprecv.buf+ads->tcprecv.used,
+ ads->tcprecv.avail-ads->tcprecv.used);
+ if (r>0) {
+ ads->tcprecv.used+= r;
+ } else {
+ if (r) {
+ if (errno==EAGAIN || errno==EWOULDBLOCK) return 0;
+ if (errno==EINTR) continue;
+ if (errno_resources(errno)) return errno;
}
- } else if (r>0) {
- ads->tcpsend.used -= r;
- memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
+ adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
+ return 0;
}
- }
- break;
+ } /* never reached */
default:
abort();
}
-
- if (callb_checkfd(maxfd,readfds,ads->udpsocket)) {
- count++;
+ if (fd == ads->udpsocket) {
for (;;) {
udpaddrlen= sizeof(udpaddr);
r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0,
(struct sockaddr*)&udpaddr,&udpaddrlen);
if (r<0) {
- if (!(errno == EAGAIN || errno == EWOULDBLOCK ||
- errno == EINTR || errno == ENOMEM || errno == ENOBUFS))
- adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
- break;
+ if (errno == EAGAIN || errno == EWOULDBLOCK) return 0;
+ if (errno == EINTR) continue;
+ if (errno_resources(errno)) return errno;
+ adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
+ return 0;
}
if (udpaddrlen != sizeof(udpaddr)) {
adns__diag(ads,-1,0,"datagram received with wrong address length %d"
inet_ntoa(udpaddr.sin_addr));
continue;
}
- adns__procdgram(ads,udpbuf,r,serv,now);
+ adns__procdgram(ads,udpbuf,r,serv,*now);
}
}
- return count;
+ return 0;
}
-int adns_callback(adns_state ads, int maxfd,
- const fd_set *readfds, const fd_set *writefds,
- const fd_set *exceptfds) {
- struct timeval now;
+int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) {
+ int r;
+
+ switch (ads->tcpstate) {
+ case server_disconnected:
+ break;
+ case server_connecting:
+ if (fd != ads->tcpsocket) break;
+ assert(ads->tcprecv.used==0);
+ for (;;) {
+ if (!adns__vbuf_ensure(&ads->tcprecv,1)) return ENOMEM;
+ r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
+ if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
+ tcp_connected(ads,*now);
+ return 0;
+ }
+ if (r>0) {
+ adns__tcp_broken(ads,"connect/read","sent data before first request");
+ return 0;
+ }
+ if (errno==EINTR) continue;
+ if (errno_resources(errno)) return errno;
+ adns__tcp_broken(ads,"connect/read",strerror(errno));
+ return 0;
+ } /* not reached */
+ case server_ok:
+ if (!(ads->tcpsend.used && fd == ads->tcpsocket)) break;
+ for (;;) {
+ adns__sigpipe_protect(ads);
+ r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
+ adns__sigpipe_unprotect(ads);
+ if (r<0) {
+ if (errno==EINTR) continue;
+ if (errno==EAGAIN || errno==EWOULDBLOCK) return 0;
+ if (errno_resources(errno)) return errno;
+ adns__tcp_broken(ads,"write",strerror(errno));
+ return 0;
+ } else if (r>0) {
+ ads->tcpsend.used -= r;
+ memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
+ }
+ } /* not reached */
+ default:
+ abort();
+ }
+ return 0;
+}
+
+int adns_processexceptional(adns_state ads, int fd, const struct timeval *now) {
+ switch (ads->tcpstate) {
+ case server_disconnected:
+ break;
+ case server_connecting:
+ case server_ok:
+ if (fd != ads->tcpsocket) break;
+ adns__tcp_broken(ads,"poll/select","exceptional condition detected");
+ return 0;
+ default:
+ abort();
+ }
+ return 0;
+}
+
+static void fd_event(adns_state ads, int fd,
+ int revent, int pollflag,
+ int maxfd, const fd_set *fds,
+ int (*func)(adns_state, int fd, const struct timeval *now),
+ struct timeval now, int *r_r) {
int r;
+
+ if (!(revent & pollflag)) return;
+ if (fds && !(fd<maxfd && FD_ISSET(fd,fds))) return;
+ r= func(ads,fd,&now);
+ if (r) {
+ if (r_r) {
+ *r_r= r;
+ } else {
+ adns__diag(ads,-1,0,"process fd failed after select: %s",strerror(errno));
+ adns_globalsystemfailure(ads);
+ }
+ }
+}
+
+void adns__fdevents(adns_state ads,
+ const struct pollfd *pollfds, int npollfds,
+ int maxfd, const fd_set *readfds,
+ const fd_set *writefds, const fd_set *exceptfds,
+ struct timeval now, int *r_r) {
+ int i, fd, revents;
+
+ for (i=0; i<npollfds; i++) {
+ fd= pollfds[i].fd;
+ if (fd >= maxfd) maxfd= fd+1;
+ revents= pollfds[i].revents;
+ fd_event(ads,fd, revents,POLLIN, maxfd,readfds, adns_processreadable,now,r_r);
+ fd_event(ads,fd, revents,POLLOUT, maxfd,writefds, adns_processwriteable,now,r_r);
+ fd_event(ads,fd, revents,POLLPRI, maxfd,exceptfds, adns_processexceptional,now,r_r);
+ }
+}
- r= gettimeofday(&now,0); if (r) return -1;
- checktimeouts(ads,now,0,0);
- return internal_callback(ads,maxfd,readfds,writefds,exceptfds,now);
+/* Wrappers for select(2). */
+
+void adns_beforeselect(adns_state ads, int *maxfd_io, fd_set *readfds_io,
+ fd_set *writefds_io, fd_set *exceptfds_io,
+ struct timeval **tv_mod, struct timeval *tv_tobuf,
+ const struct timeval *now) {
+ struct timeval tv_nowbuf;
+ struct pollfd pollfds[MAX_POLLFDS];
+ int i, fd, maxfd, npollfds;
+
+ if (tv_mod && (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) {
+ /* The caller is planning to sleep. */
+ adns__must_gettimeofday(ads,&now,&tv_nowbuf);
+ if (!now) return;
+ adns__timeouts(ads, 1, tv_mod,tv_tobuf, *now);
+ }
+
+ npollfds= adns__pollfds(ads,pollfds);
+ maxfd= *maxfd_io;
+ for (i=0; i<npollfds; i++) {
+ fd= pollfds[i].fd;
+ if (fd >= maxfd) maxfd= fd+1;
+ if (pollfds[i].events & POLLIN) FD_SET(fd,readfds_io);
+ if (pollfds[i].events & POLLOUT) FD_SET(fd,writefds_io);
+ if (pollfds[i].events & POLLPRI) FD_SET(fd,exceptfds_io);
+ }
+ *maxfd_io= maxfd;
+}
+
+void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
+ const fd_set *writefds, const fd_set *exceptfds,
+ const struct timeval *now) {
+ struct pollfd pollfds[MAX_POLLFDS];
+ int npollfds;
+
+ adns_processtimeouts(ads,now);
+
+ npollfds= adns__pollfds(ads,pollfds);
+ adns__fdevents(ads,
+ pollfds,npollfds,
+ maxfd,readfds,writefds,exceptfds,
+ *now, 0);
+}
+
+/* General helpful functions. */
+
+void adns_globalsystemfailure(adns_state ads) {
+ while (ads->timew.head) {
+ adns__query_fail(ads->timew.head, adns_s_systemfail);
+ }
+
+ switch (ads->tcpstate) {
+ case server_connecting:
+ case server_ok:
+ adns__tcp_closenext(ads);
+ break;
+ case server_disconnected:
+ break;
+ default:
+ abort();
+ }
}
-/* User-visible functions and their implementation. */
+int adns_processany(adns_state ads) {
+ int r;
+ struct timeval now;
+ struct pollfd pollfds[MAX_POLLFDS];
+ int npollfds;
+
+ r= gettimeofday(&now,0);
+ if (!r) adns_processtimeouts(ads,&now);
+
+ npollfds= adns__pollfds(ads,pollfds);
+ adns__fdevents(ads,
+ pollfds,npollfds,
+ 0,0,0,0,
+ now,&r);
+ return r;
+}
void adns__autosys(adns_state ads, struct timeval now) {
if (ads->iflags & adns_if_noautosys) return;
- adns_callback(ads,-1,0,0,0);
+ adns_processany(ads);
}
static int internal_check(adns_state ads,
adns_query *query_io,
adns_answer **answer_r,
void **context_r) {
- int r, maxfd, rsel, rcb;
+ int r, maxfd, rsel;
fd_set readfds, writefds, exceptfds;
struct timeval tvbuf, *tvp;
if (r != EWOULDBLOCK) return r;
maxfd= 0; tvp= 0;
FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
- adns_interest(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf);
+ adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf,0);
rsel= select(maxfd,&readfds,&writefds,&exceptfds,tvp);
if (rsel==-1) {
- if (errno == EINTR && !(ads->iflags & adns_if_eintr)) continue;
- return errno;
+ if (errno == EINTR) {
+ if (ads->iflags & adns_if_eintr) return EINTR;
+ } else {
+ adns__diag(ads,-1,0,"select failed in wait: %s",strerror(errno));
+ adns_globalsystemfailure(ads);
+ }
+ } else {
+ assert(rsel >= 0);
+ adns_afterselect(ads,maxfd,&readfds,&writefds,&exceptfds,0);
}
- rcb= adns_callback(ads,maxfd,&readfds,&writefds,&exceptfds);
- assert(rcb==rsel);
}
}
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);
}
#include <assert.h>
#include <unistd.h>
#include <signal.h>
+#include <errno.h>
#include <sys/time.h>
#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
#define DNS_INADDR_ARPA "in-addr", "arpa"
+#define MAX_POLLFDS ADNS_POLLFDS_RECOMMENDED
+
typedef enum {
rcode_noerror,
rcode_formaterror,
struct timeval tcptimeout;
struct sigaction stdsigpipe;
sigset_t stdsigmask;
+ struct pollfd pollfds_buf[MAX_POLLFDS];
struct server {
struct in_addr addr;
} servers[MAXSERVERS];
/* 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) {
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) \
--- /dev/null
+/*
+ * poll.c
+ * - wrappers for poll(2)
+ */
+/*
+ * This file is part of adns, which is Copyright (C) 1997-1999 Ian Jackson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <limits.h>
+
+#include "internal.h"
+
+#ifdef HAVE_POLL
+
+int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io,
+ const struct timeval *now) {
+ struct timeval tv_nowbuf, tv_tobuf, *tv_to;
+ int space, found, timeout_ms;
+ struct pollfd fds_tmp[MAX_POLLFDS];
+
+ adns__must_gettimeofday(ads,&now,&tv_nowbuf);
+ if (!now) { *nfds_io= 0; return 0; }
+
+ timeout_ms= *timeout_io;
+ if (timeout_ms == -1) {
+ tv_to= 0;
+ } else {
+ tv_tobuf.tv_sec= timeout_ms / 1000;
+ tv_tobuf.tv_usec= (timeout_ms % 1000)*1000;
+ tv_to= &tv_tobuf;
+ }
+
+ adns__timeouts(ads, 1, &tv_to,&tv_tobuf, *now);
+
+ if (tv_to) {
+ assert(tv_to == &tv_tobuf);
+ timeout_ms= (tv_tobuf.tv_usec+999)/1000;
+ assert(tv_tobuf.tv_sec < (INT_MAX-timeout_ms)/1000);
+ timeout_ms += tv_tobuf.tv_sec*1000;
+ } else {
+ timeout_ms= -1;
+ }
+ *timeout_io= timeout_ms;
+
+ space= *nfds_io;
+ if (space >= MAX_POLLFDS) {
+ found= adns__pollfds(ads,fds);
+ *nfds_io= found;
+ } else {
+ found= adns__pollfds(ads,fds_tmp);
+ *nfds_io= found;
+ if (found < space) return space ? ERANGE : 0;
+ memcpy(fds,fds_tmp,sizeof(struct pollfd)*found);
+ }
+ return 0;
+}
+
+void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
+ const struct timeval *now) {
+ struct timeval tv_buf;
+
+ adns__must_gettimeofday(ads,&now,&tv_buf);
+ if (!now) return;
+
+ adns__timeouts(ads,1, 0,0, *now);
+ adns__fdevents(ads, fds,nfds, 0,0,0,0, *now,0);
+}
+
+#endif