/* 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.
+ * Rejection of bad characters in domains (without quoteok) works.
+ * Clean up parents from adns->childw (otherwise would abort/segfault).
+ * In adnstest, allocate enough space for, and terminate, query types.
+ * In adnstest, don't print errno values as adns_status values.
+
+ * Added TODO file.
+
+ --
+
+adns (0.2) experimental; urgency=low
+
+ Portability fixes for compilation on various platforms:
+ * Include <sys/socket.h> and <netinet/in.h> in files with <arpa/inet.h>.
+ * Don't use GCC union assignment feature (.rrs=0 => .rrs.untyped=0).
+ * Explictly cast things to [const] struct sockaddr* in syscall args.
+ * Check whether we need -lsocket.
+ * Include <sys/times.h> in a few more files.
+ * Include <unistd.h> and <sys/time.h> for select.
+ * Look for inet_aton and inet_ntoa (in -lnsl and -lsocket).
+ * LDLIBS removed from dependency lists (some makes don't support this).
+ * An `ambiguous else' warning from some compilers in types.c is removed.
+
+ Other changes:
+ * Added COPYING (copy of the GPL).
+ * Regression test failure output improved.
+ * Missing targets in regress/Makefile.in added.
+ * Regression test doesn't rely on value of fcntl flags eg O_NONBLOCK.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk> Thu, 20 May 1999 00:27:32 +0100
+
adns (0.1) experimental; urgency=low
* Initial public alpha release.
fi
- echo "configure:725: checking for socket" >&5
++for ac_func in poll
++do
++echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
++echo "configure:726: checking for $ac_func" >&5
++if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
++ echo $ac_n "(cached) $ac_c" 1>&6
++else
++ cat > conftest.$ac_ext <<EOF
++#line 731 "configure"
++#include "confdefs.h"
++/* System header to define __stub macros and hopefully few prototypes,
++ which can conflict with char $ac_func(); below. */
++#include <assert.h>
++/* Override any gcc2 internal prototype to avoid an error. */
++/* We use char because int might match the return type of a gcc2
++ builtin and then its argument prototype would still apply. */
++char $ac_func();
++
++int main() {
++
++/* The GNU C library defines this for functions which it implements
++ to always fail with ENOSYS. Some functions are actually named
++ something starting with __ and the normal name is an alias. */
++#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
++choke me
++#else
++$ac_func();
++#endif
++
++; return 0; }
++EOF
++if { (eval echo configure:754: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++ rm -rf conftest*
++ eval "ac_cv_func_$ac_func=yes"
++else
++ echo "configure: failed program was:" >&5
++ cat conftest.$ac_ext >&5
++ rm -rf conftest*
++ eval "ac_cv_func_$ac_func=no"
++fi
++rm -f conftest*
++fi
++
++if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
++ echo "$ac_t""yes" 1>&6
++ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
++ cat >> confdefs.h <<EOF
++#define $ac_tr_func 1
++EOF
++
++else
++ echo "$ac_t""no" 1>&6
++fi
++done
++
+
+ echo $ac_n "checking for socket""... $ac_c" 1>&6
- #line 730 "configure"
++echo "configure:780: checking for socket" >&5
+if eval "test \"`echo '$''{'ac_cv_func_socket'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:753: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 785 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char socket(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char socket();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_socket) || defined (__stub___socket)
+choke me
+#else
+socket();
+#endif
+
+; return 0; }
+EOF
- echo "configure:772: checking for socket in -lsocket" >&5
++if { (eval echo configure:808: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_socket=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_socket=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'socket`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+
+ echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
- #line 780 "configure"
++echo "configure:827: checking for socket in -lsocket" >&5
+ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lsocket $LIBS"
+cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:791: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 835 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
- echo "configure:827: checking for inet_ntoa" >&5
++if { (eval echo configure:846: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo socket | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lsocket $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+
+ { echo "configure: error: cannot find library function socket" 1>&2; exit 1; }
+
+fi
+
+
+fi
+
+
+
+ echo $ac_n "checking for inet_ntoa""... $ac_c" 1>&6
- #line 832 "configure"
++echo "configure:882: checking for inet_ntoa" >&5
+if eval "test \"`echo '$''{'ac_cv_func_inet_ntoa'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:855: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 887 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char inet_ntoa(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char inet_ntoa();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_inet_ntoa) || defined (__stub___inet_ntoa)
+choke me
+#else
+inet_ntoa();
+#endif
+
+; return 0; }
+EOF
- echo "configure:874: checking for inet_ntoa in -lnsl" >&5
++if { (eval echo configure:910: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_inet_ntoa=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_inet_ntoa=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'inet_ntoa`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+
+ echo $ac_n "checking for inet_ntoa in -lnsl""... $ac_c" 1>&6
- #line 882 "configure"
++echo "configure:929: checking for inet_ntoa in -lnsl" >&5
+ac_lib_var=`echo nsl'_'inet_ntoa | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lnsl $LIBS"
+cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:893: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 937 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char inet_ntoa();
+
+int main() {
+inet_ntoa()
+; return 0; }
+EOF
- echo "configure:930: checking for inet_aton" >&5
++if { (eval echo configure:948: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo nsl | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lnsl $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+
+ { echo "configure: error: cannot find library function inet_ntoa" 1>&2; exit 1; }
+
+fi
+
+
+fi
+
+
+
+
+ echo $ac_n "checking for inet_aton""... $ac_c" 1>&6
- #line 935 "configure"
++echo "configure:985: checking for inet_aton" >&5
+if eval "test \"`echo '$''{'ac_cv_func_inet_aton'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:958: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 990 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char inet_aton(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char inet_aton();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_inet_aton) || defined (__stub___inet_aton)
+choke me
+#else
+inet_aton();
+#endif
+
+; return 0; }
+EOF
- echo "configure:977: checking for inet_aton in -lresolv" >&5
++if { (eval echo configure:1013: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_func_inet_aton=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_inet_aton=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'inet_aton`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ :
+else
+ echo "$ac_t""no" 1>&6
+
+ echo $ac_n "checking for inet_aton in -lresolv""... $ac_c" 1>&6
- #line 985 "configure"
++echo "configure:1032: checking for inet_aton in -lresolv" >&5
+ac_lib_var=`echo resolv'_'inet_aton | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lresolv $LIBS"
+cat > conftest.$ac_ext <<EOF
- if { (eval echo configure:996: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
++#line 1040 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char inet_aton();
+
+int main() {
+inet_aton()
+; return 0; }
+EOF
++if { (eval echo configure:1051: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+
+ LIBS="-lresolv $LIBS";
+ echo "configure: warning: inet_aton is in libresolv, urgh. Must use -lresolv." 1>&2
+
+else
+ echo "$ac_t""no" 1>&6
+
+ { echo "configure: error: cannot find library function inet_aton" 1>&2; exit 1; }
+
+fi
+
+
+fi
+
+
+
+
echo $ac_n "checking __attribute__((,,))""... $ac_c" 1>&6
- echo "configure:1030: checking __attribute__((,,))" >&5
-echo "configure:725: checking __attribute__((,,))" >&5
++echo "configure:1085: checking __attribute__((,,))" >&5
if eval "test \"`echo '$''{'adns_cv_c_attribute_supported'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
- #line 1036 "configure"
-#line 731 "configure"
++#line 1091 "configure"
#include "confdefs.h"
int main() {
extern int testfunction(int x) __attribute__((,,))
; return 0; }
EOF
- if { (eval echo configure:1043: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
-if { (eval echo configure:738: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
++if { (eval echo configure:1098: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
adns_cv_c_attribute_supported=yes
else
echo $ac_n "checking __attribute__((noreturn))""... $ac_c" 1>&6
- echo "configure:1065: checking __attribute__((noreturn))" >&5
-echo "configure:760: checking __attribute__((noreturn))" >&5
++echo "configure:1120: checking __attribute__((noreturn))" >&5
if eval "test \"`echo '$''{'adns_cv_c_attribute_noreturn'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
- #line 1071 "configure"
-#line 766 "configure"
++#line 1126 "configure"
#include "confdefs.h"
int main() {
extern int testfunction(int x) __attribute__((noreturn))
; return 0; }
EOF
- if { (eval echo configure:1078: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
-if { (eval echo configure:773: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
++if { (eval echo configure:1133: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
adns_cv_c_attribute_noreturn=yes
else
echo $ac_n "checking __attribute__((const))""... $ac_c" 1>&6
- echo "configure:1105: checking __attribute__((const))" >&5
-echo "configure:800: checking __attribute__((const))" >&5
++echo "configure:1160: checking __attribute__((const))" >&5
if eval "test \"`echo '$''{'adns_cv_c_attribute_const'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
- #line 1111 "configure"
-#line 806 "configure"
++#line 1166 "configure"
#include "confdefs.h"
int main() {
extern int testfunction(int x) __attribute__((const))
; return 0; }
EOF
- if { (eval echo configure:1118: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
-if { (eval echo configure:813: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
++if { (eval echo configure:1173: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
adns_cv_c_attribute_const=yes
else
echo $ac_n "checking __attribute__((format...))""... $ac_c" 1>&6
- echo "configure:1145: checking __attribute__((format...))" >&5
-echo "configure:840: checking __attribute__((format...))" >&5
++echo "configure:1200: checking __attribute__((format...))" >&5
if eval "test \"`echo '$''{'adns_cv_attribute_format'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
- #line 1151 "configure"
-#line 846 "configure"
++#line 1206 "configure"
#include "confdefs.h"
int main() {
extern int testfunction(char *y, ...) __attribute__((format(printf,1,2)))
; return 0; }
EOF
- if { (eval echo configure:1158: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
-if { (eval echo configure:853: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
++if { (eval echo configure:1213: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
rm -rf conftest*
adns_cv_attribute_format=yes
else
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)
/* 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);
-/* You may call this even if you have queries outstanding;
- * they will be cancelled.
++/* The adns_query you get back from _submit is valid (ie, can be
++ * legitimately passed into adns functions) until it is returned by
++ * adns_check or adns_wait, or passed to adns_cancel. After that it
++ * must not be used. You can rely on it not being reused until the
++ * first adns_submit or _transact call using the same adns_state after
++ * it became invalid, so you may compare it for equality with other
++ * query handles until you next call _query or _transact.
+ */
+
-int adns_callback(adns_state, int maxfd, const fd_set *readfds, const fd_set *writefds,
- const fd_set *exceptfds);
-/* Gives adns flow-of-control for a bit. This will never block.
- * If maxfd == -1 then adns will check (make nonblocking system calls on)
- * all of its own filedescriptors; otherwise it will only use those
- * < maxfd and specified in the fd_set's, as if select had returned them.
- * Other fd's may be in the fd_sets, and will be ignored.
- * _callback returns how many adns fd's were in the various sets, so
- * you can tell if your select handling code has missed something and is going awol.
- *
- * May also return -1 if a critical syscall failed, setting errno.
++void adns_finish(adns_state ads);
+/* You may call this even if you have queries outstanding;
+ * they will be cancelled.
*/
- int adns_callback(adns_state, int maxfd, const fd_set *readfds, const fd_set *writefds,
- const fd_set *exceptfds);
- /* Gives adns flow-of-control for a bit. This will never block.
- * If maxfd == -1 then adns will check (make nonblocking system calls on)
- * all of its own filedescriptors; otherwise it will only use those
- * < maxfd and specified in the fd_set's, as if select had returned them.
- * Other fd's may be in the fd_sets, and will be ignored.
- * _callback returns how many adns fd's were in the various sets, so
- * you can tell if your select handling code has missed something and is going awol.
- *
- * May also return -1 if a critical syscall failed, setting errno.
-void adns_interest(adns_state, int *maxfd_io, fd_set *readfds_io,
- fd_set *writefds_io, fd_set *exceptfds_io,
- struct timeval **tv_mod, struct timeval *tv_buf);
-/* Find out file descriptors adns is interested in, and when it
- * would like the opportunity to time something out. If you do not plan to
- * block then tv_mod may be 0. Otherwise, tv_mod may point to 0 meaning
- * you have no timeout of your own, in which case tv_buf must be non-null and
- * _interest may fill it in and set *tv_mod=tv_buf.
- * readfds, writefds, exceptfds and maxfd may not be 0.
-- */
- void adns_interest(adns_state, int *maxfd_io, fd_set *readfds_io,
- fd_set *writefds_io, fd_set *exceptfds_io,
- struct timeval **tv_mod, struct timeval *tv_buf);
- /* Find out file descriptors adns is interested in, and when it
- * would like the opportunity to time something out. If you do not plan to
- * block then tv_mod may be 0. Otherwise, tv_mod may point to 0 meaning
- * you have no timeout of your own, in which case tv_buf must be non-null and
- * _interest may fill it in and set *tv_mod=tv_buf.
- * readfds, writefds, exceptfds and maxfd may not be 0.
-struct pollfd *adns_pollfds(adns_state, struct pollfd *buf, int *len_io, int *timeout);
-/* Like adns_interest, but for use with poll(2). The return value
- * will be a pointer to an array of struct pollfd, one for each of
- * adns's fds, and *len_io will be set to the number of file
- * descriptors.
- *
- * Each descriptor's entry will have a revents==0, and events will be
- * a combination of one or more of POLLIN, POLLOUT and POLLERR.
- *
- * *timeout will be adjusted to ensure that it is no more than the
- * desired timeout (with the usual rule for poll that <0 stands for
- * infinity). *timeout may be 0 in which case no timeout information
- * will be returned.
- *
- * If *len_io is nonnegative then adns_pollfds will write to buffer
- * provided by the caller in buf. If it is too small then *len_io
- * will be updated to say how large it would have needed to be and
- * adns_pollfds will fail with ENOSPC.
- *
- * If *len_io is -1 then buf should be 0, and adns_pollfds will use some
- * memory belonging to the adns_state, which may be overwritten on subsequent
- * calls.
- *
- * When you come out of poll(2), you should probably call adns_callback
- * with maxfd==-1.
++struct adns_query adns_forallqueries_begin(adns_state ads, void **context_r);
++struct adns_query adns_forallqueries_next(adns_state ads, adns_query, void **context_r);
++/* Iterator functions, which you can use to loop over the outstanding
++ * (submitted but not yet successfuly checked/waited) queries. Each
++ * function returns a query handle and a corresponding context pointer,
++ * or returns 0 setting *context_r to 0 if there are no (more) queries.
++ * There is no need to explicitly finish an iteration. context_r may be 0.
+ *
- * On systems without poll(2) this function will return ENOSYS.
++ * IMPORTANT: you MUST NOT call ANY other adns function with the same
++ * adns_state, or with a query in the same adns_state, while you are
++ * doing one of these iterations. After such a call the iterator
++ * value has undefined meaning and must not be used.
*/
--/* Example expected/legal calling sequences:
++/*
++ * Example expected/legal calling sequence for submit/check/wait:
* adns_init
* adns_submit 1
* adns_submit 2
* adns_wait 3
* ....
* adns_finish
++ */
++
++/*
++ * Entrypoints for generic asynch io:
++ * (these entrypoints are not very useful except in combination with *
++ * some of the other I/O model calls which can tell you which fds to
++ * be interested in):
++ *
++ * Note that any adns call may cause adns to open and close fds, so
++ * you must call beforeselect or beforepoll again just before
++ * blocking, or you may not have an up-to-date list of it's fds.
++ */
++
++int adns_processany(adns_state ads);
++/* Gives adns flow-of-control for a bit. This will never block, and
++ * can be used with any threading/asynch-io model. If some error
++ * occurred which might cause an event loop to spin then the errno
++ * value is returned.
++ */
++
++int adns_processreadable(adns_state ads, int fd, const struct timeval *now);
++int adns_processwriteable(adns_state ads, int fd, const struct timeval *now);
++int adns_processexceptional(adns_state ads, int fd, const struct timeval *now);
++/* Gives adns flow-of-control so that it can process incoming data
++ * from, or send outgoing data via, fd. Very like _processany. If it
++ * returns zero then fd will no longer be readable or writeable
++ * (unless of course more data has arrived since). adns will _only_
++ * that fd and only in the manner specified, regardless of whether
++ * adns_if_noautosys was specified.
++ *
++ * adns_processexceptional should be called when select(2) reports an
++ * exceptional condition, or poll(2) reports POLLPRI.
++ *
++ * It is fine to call _processreabable or _processwriteable when the
++ * fd is not ready, or with an fd that doesn't belong to adns; it will
++ * then just return 0.
++ *
++ * If some error occurred which might prevent an event loop to spin
++ * then the errno value is returned.
++ */
++
++void adns_processtimeouts(adns_state ads, const struct timeval *now);
++/* Gives adns flow-of-control so that it can process any timeouts
++ * which might have happened. Very like _processreadable/writeable.
++ *
++ * now may be 0; if it isn't, *now must be the current time, recently
++ * obtained from gettimeofday.
++ */
++
++void adns_firsttimeout(adns_state ads,
++ struct timeval **tv_mod, struct timeval *tv_buf,
++ struct timeval now);
++/* Asks adns when it would first like the opportunity to time
++ * something out. now must be the current time, from gettimeofday.
++ *
++ * If tv_mod points to 0 then tv_buf must be non-null, and
++ * _firsttimeout will fill in *tv_buf with the time until the first
++ * timeout, and make *tv_mod point to tv_buf. If adns doesn't have
++ * anything that might need timing out it will leave *tv_mod as 0.
++ *
++ * If *tv_mod is not 0 then tv_buf is not used. adns will update
++ * *tv_mod if it has any earlier timeout, and leave it alone if it
++ * doesn't.
++ *
++ * This call will not actually do any I/O, or change the fds that adns
++ * is using. It always succeeds and never blocks.
++ */
++
++void adns_globalsystemfailure(adns_state ads);
++/* If serious problem(s) happen which globally affect your ability to
++ * interact properly with adns, or adns's ability to function
++ * properly, you or adns can call this function.
++ *
++ * All currently outstanding queries will be made to fail with
++ * adns_s_systemfail, and adns will close any stream sockets it has
++ * open.
++ *
++ * This is used by adns, for example, if gettimeofday() fails.
++ * Without this the program's event loop might start to spin !
++ *
++ * This call will never block.
++ */
++
++/*
++ * Entrypoints for select-loop based asynch io:
++ */
++
++void adns_beforeselect(adns_state ads, int *maxfd, fd_set *readfds,
++ fd_set *writefds, fd_set *exceptfds,
++ struct timeval **tv_mod, struct timeval *tv_buf,
++ const struct timeval *now);
++/* Find out file descriptors adns is interested in, and when it would
++ * like the opportunity to time something out. If you do not plan to
++ * block then tv_mod may be 0. Otherwise, tv_mod and tv_buf are as
++ * for adns_firsttimeout. readfds, writefds, exceptfds and maxfd_io may
++ * not be 0.
++ *
++ * If *now is not 0 then this will never actually do any I/O, or
++ * change the fds that adns is using or the timeouts it wants. In any
++ * case it won't block.
++ */
++
++void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
++ const fd_set *writefds, const fd_set *exceptfds,
++ const struct timeval *now);
++/* Gives adns flow-of-control for a bit; intended for use after
++ * select. This is just a fancy way of calling adns_processreadable/
++ * writeable/timeouts as appropriate, as if select had returned the
++ * data being passed. Always succeeds.
++ */
++
++/*
++ * Example calling sequence:
*
* adns_init _noautosys
* loop {
-- * adns_interest
++ * adns_beforeselect
* select
-- * adns_callback
++ * adns_afterselect
* ...
* adns_submit / adns_check
* ...
* }
*/
++/*
++ * Entrypoints for poll-loop based asynch io:
++ */
++
++struct pollfd;
++/* In case your system doesn't have it or you forgot to include
++ * <sys/poll.h>, to stop the following declarations from causing
++ * problems. If your system doesn't have poll then the following
++ * entrypoints will not be defined in libadns. Sorry !
++ */
++
++int adns_beforepoll(adns_state ads, struct pollfd *fds, int *nfds_io, int *timeout_io,
++ const struct timeval *now);
++/* Finds out which fd's adns is interested in, and when it would like
++ * to be able to time things out. This is in a form suitable for use
++ * with poll(2).
++ *
++ * On entry, usually fds should point to at least *nfds_io structs.
++ * adns will fill up to that many structs will information for poll,
++ * and record in *nfds_io how many structs it filled. If it wants to
++ * listen for more structs then *nfds_io will be set to the number
++ * required and _beforepoll will return ERANGE.
++ *
++ * NOTE that if now is 0, adns may acquire additional fds from one
++ * call to the next, so you must put adns_beforepoll in a loop, rather
++ * than assuming that the second call (with the buffer size requested
++ * by the first) will not return ERANGE.
++ *
++ * You may call _beforepoll with fds==0 and *nfds_io 0, in which case
++ * adns will fill in the number of fds that it might be interested in
++ * in *nfds_io, and always return 0.
++ *
++ * adns only ever sets POLLIN, POLLOUT and POLLPRI in its pollfd
++ * structs, and only ever looks at those bits. POLLPRI is required to
++ * detect TCP Urgent Data, which should not be used by a DNS server,
++ * so that adns can know that the TCP stream is now useless.
++ *
++ * In any case, *timeout_io should be a timeout value as for poll(2),
++ * which adns will modify downwards as required. If the caller does
++ * not plan to block then *timeout_io should be 0 on entry.
++ *
++ * adns_beforepoll will return 0 on success, and will not fail for any
++ * reason other than the fds buffer being too small (ERANGE).
++ *
++ * If *now is not 0 then this call will never actually do any I/O, or
++ * change the fds that adns is using or the timeouts it wants. In any
++ * case it won't block.
++ */
++
++#define ADNS_POLLFDS_RECOMMENDED 2
++/* If you allocate an fds buf with at least RECOMMENDED entries then
++ * you are unlikely to need to enlarge it. You are recommended to do
++ * so if it's convenient. However, you must be prepared for adns to
++ * require more space than this.
++ */
++
++void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
++ const struct timeval *now);
++/* Gives adns flow-of-control for a bit; intended for use after
++ * poll(2). fds and nfds should be the results from poll(). pollfd
++ * structs mentioning fds not belonging to adns will be ignored.
++ */
++
++
adns_status adns_rr_info(adns_rrtype type,
const char **rrtname_r, const char **fmtname_r,
int *len_r,
*/
const char *adns_strerror(adns_status st);
+const char *adns_errabbrev(adns_status st);
++/* Like strerror but for adns_status values. adns_errabbrev returns
++ * the abbreviation of the error - eg, for adns_s_timeout it returns
++ * "timeout". You MUST NOT call these functions with status values
++ * not returned by the same adns library.
++ */
#endif
# 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
+
+/* Define if you have the socket library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
/* Use the definitions: */
++#ifdef HAVE_POLL
++#include <sys/poll.h>
++#else
++struct pollfd { int fd; short events; short revents; };
++#define POLLIN 1
++#define POLLPRI 2
++#define POLLOUT 4
++#endif
++
/* GNU C attributes. */
#ifndef FUNCATTR
#ifdef HAVE_GNUC25_ATTRIB
#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);
+
-static void checkfds(adns_state ads, int *maxfd,
- fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
- int avail, struct pollfd *buf, int *count_r) {
- int count;
++void adns_firsttimeout(adns_state ads,
++ struct timeval **tv_io, struct timeval *tvbuf,
++ struct timeval now) {
++ adns__timeouts(ads, 0, tv_io,tvbuf, now);
++}
++
++void adns_processtimeouts(adns_state ads, const struct timeval *now) {
++ struct timeval tv_buf;
++
++ adns__must_gettimeofday(ads,&now,&tv_buf); if (!now) return;
++ adns__timeouts(ads, 1, 0,0, *now);
+ }
+
- count= 0;
- inter_addfd(ads->udpsocket,maxfd,readfds);
- poll_addfd(ads->udpsocket,avail,buf,&count,POLLIN);
++/* fd handling functions. These are the top-level of the real work of
++ * reception and often transmission.
++ */
++
++int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]) {
++ /* Returns the number of entries filled in. Always zeroes revents. */
++
++ assert(MAX_POLLFDS==2);
+
++ pollfds_buf[0].fd= ads->udpsocket;
++ pollfds_buf[0].events= POLLIN;
++ pollfds_buf[0].revents= 0;
switch (ads->tcpstate) {
case server_disconnected:
-- break;
++ return 1;
case server_connecting:
- inter_addfd(maxfd,writefds,ads->tcpsocket);
- inter_addfd(ads->tcpsocket,maxfd,writefds);
- poll_addfd(ads->tcpsocket,avail,buf,&count,POLLOUT);
++ pollfds_buf[1].events= POLLOUT;
break;
case server_ok:
- inter_addfd(maxfd,readfds,ads->tcpsocket);
- inter_addfd(maxfd,exceptfds,ads->tcpsocket);
- if (ads->tcpsend.used) inter_addfd(maxfd,writefds,ads->tcpsocket);
- inter_addfd(ads->tcpsocket,maxfd,readfds);
- inter_addfd(ads->tcpsocket,maxfd,exceptfds);
- if (ads->tcpsend.used) inter_addfd(ads->tcpsocket,maxfd,writefds);
- poll_addfd(ads->tcpsocket,avail,buf,&count,
- ads->tcpsend.used ? POLLIN|POLLOUT : POLLIN);
++ pollfds_buf[1].events= ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI;
break;
default:
abort();
}
++ pollfds_buf[1].fd= ads->tcpsocket;
++ pollfds_buf[1].revents= 0;
++ return 2;
}
-struct pollfd *adns_pollfds(adns_state ads, struct pollfd *buf,
- int *len_io, int *timeout_io) {
- struct timeval now, tvbuf, *tvp;
- int timeout;
-
- r= gettimeofday(&now,0);
- if (r) return 0;
-
- timeout= *timeout_io;
- if (timeout < 0) {
- tvtop= 0;
- } else {
- tvbuf.tv_sec= now.tv_sec + (timeout/1000);
- tvbuf.tv_usec= now.tv_usec + (timeout%1000)*1000;
- if (tvbuf.tv_sec >= 1000000) {
- tvbuf.tv_sec += 1;
- tvbuf.tv_usec -= 1000000;
- }
- tvp= &tvbuf;
- }
- checktimouts(ads,now,&tvp,&tvbuf);
-
- if (tvp) {
- assert(tvbuf.tv_sec<INT_MAX/1000);
- *timeout_io= (tvp->tv_sec - now.tv_sec)*1000 + (tvp->tv_usec - now.tv_usec)/1000;
- } else {
- *timeout_io= -1;
- }
-
- avail= *len_io;
- if (avail == -1) {
- buf= ads->pollfdsbuf;
- avail= 2;
- if (!buf) {
- buf= ads->pollfdsbuf= malloc(sizeof(struct pollfd)*avail);
- if (!buf) return 0;
- }
- }
- checkfds(ads, 0,0,0,0, avail,buf,len_io);
- if (*len_io > avail) { errno= ENOSPC; return 0; }
-
- return buf;
-}
-
-void adns_interest(adns_state ads, int *maxfd,
- fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
- struct timeval **tv_io, struct timeval *tvbuf) {
- struct timeval now;
- struct timeval tvto_lr;
- int r;
-
-/*fprintf(stderr,"adns_interest\n");*/
-
- r= gettimeofday(&now,0);
- if (r) {
- adns__warn(ads,-1,0,"gettimeofday failed - will sleep for a bit: %s",
- strerror(errno));
- timerclear(&tvto_lr); timevaladd(&tvto_lr,LOCALRESOURCEMS);
- inter_maxto(tv_io, tvbuf, tvto_lr);
- } else {
- checktimeouts(ads,now,tv_io,tvbuf);
- }
-
- checkfds(ads, maxfd,readfds,writefds,exceptfds, 0,0,0);
-}
-
--/* Callback procedures - these do the real work of reception and timeout, etc. */
--
--static int callb_checkfd(int maxfd, const fd_set *fds, int fd) {
-- return maxfd<0 || !fds ? 1 :
-- fd<maxfd && FD_ISSET(fd,fds);
--}
--
--static int internal_callback(adns_state ads, int maxfd,
-- const fd_set *readfds, const fd_set *writefds,
-- const fd_set *exceptfds,
-- struct timeval now) {
-- int skip, want, dgramlen, count, udpaddrlen, r, serv;
++int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
++ int skip, want, dgramlen, r, udpaddrlen, serv;
byte udpbuf[DNS_MAXUDP];
struct sockaddr_in udpaddr;
--
-- count= 0;
--
++
switch (ads->tcpstate) {
case server_disconnected:
-- break;
case server_connecting:
-- if (callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
-- count++;
-- assert(ads->tcprecv.used==0);
-- if (!adns__vbuf_ensure(&ads->tcprecv,1)) return -1;
-- if (ads->tcprecv.buf) {
-- r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
-- if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
-- tcp_connected(ads,now);
-- } else if (r>0) {
-- adns__tcp_broken(ads,"connect/read","sent data before first request");
-- } else if (errno!=EINTR) {
-- adns__tcp_broken(ads,"connect/read",strerror(errno));
-- }
-- }
-- }
break;
case server_ok:
-- count+= callb_checkfd(maxfd,readfds,ads->tcpsocket) +
-- callb_checkfd(maxfd,exceptfds,ads->tcpsocket) +
-- (ads->tcpsend.used && callb_checkfd(maxfd,writefds,ads->tcpsocket));
-- if (callb_checkfd(maxfd,readfds,ads->tcpsocket)) {
-- skip= 0;
-- for (;;) {
-- if (ads->tcprecv.used<skip+2) {
-- want= 2;
-- } else {
-- dgramlen= (ads->tcprecv.buf[skip]<<8) | ads->tcprecv.buf[skip+1];
-- if (ads->tcprecv.used<skip+2+dgramlen) {
-- want= 2+dgramlen;
-- } else {
-- adns__procdgram(ads,ads->tcprecv.buf+skip+2,dgramlen,ads->tcpserver,now);
-- skip+= 2+dgramlen; continue;
-- }
-- }
-- ads->tcprecv.used -= skip;
-- memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
-- skip= 0;
-- if (!adns__vbuf_ensure(&ads->tcprecv,want)) return -1;
-- assert(ads->tcprecv.used <= ads->tcprecv.avail);
-- if (ads->tcprecv.used == ads->tcprecv.avail) continue;
-- r= read(ads->tcpsocket,
-- ads->tcprecv.buf+ads->tcprecv.used,
-- ads->tcprecv.avail-ads->tcprecv.used);
-- if (r>0) {
-- ads->tcprecv.used+= r;
++ if (fd != ads->tcpsocket) break;
++ skip= 0;
++ for (;;) {
++ if (ads->tcprecv.used<skip+2) {
++ want= 2;
++ } else {
++ dgramlen= (ads->tcprecv.buf[skip]<<8) | ads->tcprecv.buf[skip+1];
++ if (ads->tcprecv.used<skip+2+dgramlen) {
++ want= 2+dgramlen;
} else {
-- if (r<0) {
-- if (errno==EAGAIN || errno==EWOULDBLOCK || errno==ENOMEM) break;
-- if (errno==EINTR) continue;
-- }
-- adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
-- break;
++ adns__procdgram(ads,ads->tcprecv.buf+skip+2,dgramlen,ads->tcpserver,*now);
++ skip+= 2+dgramlen; continue;
}
}
-- } else if (callb_checkfd(maxfd,exceptfds,ads->tcpsocket)) {
-- adns__tcp_broken(ads,"select","exceptional condition detected");
-- } else if (ads->tcpsend.used && callb_checkfd(maxfd,writefds,ads->tcpsocket)) {
- adns__sigpipe_protect(ads);
-- r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
- adns__sigpipe_unprotect(ads);
-- if (r<0) {
-- if (errno!=EAGAIN && errno!=EWOULDBLOCK && errno!=ENOMEM && errno!=EINTR) {
-- adns__tcp_broken(ads,"write",strerror(errno));
++ ads->tcprecv.used -= skip;
++ memmove(ads->tcprecv.buf,ads->tcprecv.buf+skip,ads->tcprecv.used);
++ skip= 0;
++ if (!adns__vbuf_ensure(&ads->tcprecv,want)) return ENOMEM;
++ assert(ads->tcprecv.used <= ads->tcprecv.avail);
++ if (ads->tcprecv.used == ads->tcprecv.avail) continue;
++ r= read(ads->tcpsocket,
++ ads->tcprecv.buf+ads->tcprecv.used,
++ ads->tcprecv.avail-ads->tcprecv.used);
++ if (r>0) {
++ ads->tcprecv.used+= r;
++ } else {
++ if (r) {
++ if (errno==EAGAIN || errno==EWOULDBLOCK) return 0;
++ if (errno==EINTR) continue;
++ if (errno_resources(errno)) return errno;
}
-- } else if (r>0) {
-- ads->tcpsend.used -= r;
-- memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
++ adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
++ return 0;
}
-- }
-- break;
++ } /* never reached */
default:
abort();
}
--
-- if (callb_checkfd(maxfd,readfds,ads->udpsocket)) {
-- count++;
++ if (fd == ads->udpsocket) {
for (;;) {
udpaddrlen= sizeof(udpaddr);
- r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0,&udpaddr,&udpaddrlen);
+ r= recvfrom(ads->udpsocket,udpbuf,sizeof(udpbuf),0,
+ (struct sockaddr*)&udpaddr,&udpaddrlen);
if (r<0) {
-- if (!(errno == EAGAIN || errno == EWOULDBLOCK ||
-- errno == EINTR || errno == ENOMEM || errno == ENOBUFS))
-- adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
-- break;
++ if (errno == EAGAIN || errno == EWOULDBLOCK) return 0;
++ if (errno == EINTR) continue;
++ if (errno_resources(errno)) return errno;
++ adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
++ return 0;
}
if (udpaddrlen != sizeof(udpaddr)) {
adns__diag(ads,-1,0,"datagram received with wrong address length %d"
inet_ntoa(udpaddr.sin_addr));
continue;
}
-- adns__procdgram(ads,udpbuf,r,serv,now);
++ adns__procdgram(ads,udpbuf,r,serv,*now);
}
}
-- return count;
++ return 0;
}
--int adns_callback(adns_state ads, int maxfd,
-- const fd_set *readfds, const fd_set *writefds,
-- const fd_set *exceptfds) {
-- struct timeval now;
++int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) {
+ int r;
++
++ switch (ads->tcpstate) {
++ case server_disconnected:
++ break;
++ case server_connecting:
++ if (fd != ads->tcpsocket) break;
++ assert(ads->tcprecv.used==0);
++ for (;;) {
++ if (!adns__vbuf_ensure(&ads->tcprecv,1)) return ENOMEM;
++ r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
++ if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
++ tcp_connected(ads,*now);
++ return 0;
++ }
++ if (r>0) {
++ adns__tcp_broken(ads,"connect/read","sent data before first request");
++ return 0;
++ }
++ if (errno==EINTR) continue;
++ if (errno_resources(errno)) return errno;
++ adns__tcp_broken(ads,"connect/read",strerror(errno));
++ return 0;
++ } /* not reached */
++ case server_ok:
++ if (!(ads->tcpsend.used && fd == ads->tcpsocket)) break;
++ for (;;) {
++ adns__sigpipe_protect(ads);
++ r= write(ads->tcpsocket,ads->tcpsend.buf,ads->tcpsend.used);
++ adns__sigpipe_unprotect(ads);
++ if (r<0) {
++ if (errno==EINTR) continue;
++ if (errno==EAGAIN || errno==EWOULDBLOCK) return 0;
++ if (errno_resources(errno)) return errno;
++ adns__tcp_broken(ads,"write",strerror(errno));
++ return 0;
++ } else if (r>0) {
++ ads->tcpsend.used -= r;
++ memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
++ }
++ } /* not reached */
++ default:
++ abort();
++ }
++ return 0;
++}
++
++int adns_processexceptional(adns_state ads, int fd, const struct timeval *now) {
++ switch (ads->tcpstate) {
++ case server_disconnected:
++ break;
++ case server_connecting:
++ case server_ok:
++ if (fd != ads->tcpsocket) break;
++ adns__tcp_broken(ads,"poll/select","exceptional condition detected");
++ return 0;
++ default:
++ abort();
++ }
++ return 0;
++}
+
- r= gettimeofday(&now,0); if (r) return -1;
- checktimeouts(ads,now,0,0);
- return internal_callback(ads,maxfd,readfds,writefds,exceptfds,now);
++static void fd_event(adns_state ads, int fd,
++ int revent, int pollflag,
++ int maxfd, const fd_set *fds,
++ int (*func)(adns_state, int fd, const struct timeval *now),
++ struct timeval now, int *r_r) {
+ int r;
++
++ if (!(revent & pollflag)) return;
++ if (fds && !(fd<maxfd && FD_ISSET(fd,fds))) return;
++ r= func(ads,fd,&now);
++ if (r) {
++ if (r_r) {
++ *r_r= r;
++ } else {
++ adns__diag(ads,-1,0,"process fd failed after select: %s",strerror(errno));
++ adns_globalsystemfailure(ads);
++ }
++ }
++}
++
++void adns__fdevents(adns_state ads,
++ const struct pollfd *pollfds, int npollfds,
++ int maxfd, const fd_set *readfds,
++ const fd_set *writefds, const fd_set *exceptfds,
++ struct timeval now, int *r_r) {
++ int i, fd, revents;
++
++ for (i=0; i<npollfds; i++) {
++ fd= pollfds[i].fd;
++ if (fd >= maxfd) maxfd= fd+1;
++ revents= pollfds[i].revents;
++ fd_event(ads,fd, revents,POLLIN, maxfd,readfds, adns_processreadable,now,r_r);
++ fd_event(ads,fd, revents,POLLOUT, maxfd,writefds, adns_processwriteable,now,r_r);
++ fd_event(ads,fd, revents,POLLPRI, maxfd,exceptfds, adns_processexceptional,now,r_r);
++ }
++}
+
- r= gettimeofday(&now,0); if (r) return -1;
- checktimeouts(ads,now,0,0);
- return internal_callback(ads,maxfd,readfds,writefds,exceptfds,now);
++/* Wrappers for select(2). */
++
++void adns_beforeselect(adns_state ads, int *maxfd_io, fd_set *readfds_io,
++ fd_set *writefds_io, fd_set *exceptfds_io,
++ struct timeval **tv_mod, struct timeval *tv_tobuf,
++ const struct timeval *now) {
++ struct timeval tv_nowbuf;
++ struct pollfd pollfds[MAX_POLLFDS];
++ int i, fd, maxfd, npollfds;
++
++ if (tv_mod && (!*tv_mod || (*tv_mod)->tv_sec || (*tv_mod)->tv_usec)) {
++ /* The caller is planning to sleep. */
++ adns__must_gettimeofday(ads,&now,&tv_nowbuf);
++ if (!now) return;
++ adns__timeouts(ads, 1, tv_mod,tv_tobuf, *now);
++ }
++
++ npollfds= adns__pollfds(ads,pollfds);
++ maxfd= *maxfd_io;
++ for (i=0; i<npollfds; i++) {
++ fd= pollfds[i].fd;
++ if (fd >= maxfd) maxfd= fd+1;
++ if (pollfds[i].events & POLLIN) FD_SET(fd,readfds_io);
++ if (pollfds[i].events & POLLOUT) FD_SET(fd,writefds_io);
++ if (pollfds[i].events & POLLPRI) FD_SET(fd,exceptfds_io);
++ }
++ *maxfd_io= maxfd;
++}
++
++void adns_afterselect(adns_state ads, int maxfd, const fd_set *readfds,
++ const fd_set *writefds, const fd_set *exceptfds,
++ const struct timeval *now) {
++ struct pollfd pollfds[MAX_POLLFDS];
++ int npollfds;
++
++ adns_processtimeouts(ads,now);
++
++ npollfds= adns__pollfds(ads,pollfds);
++ adns__fdevents(ads,
++ pollfds,npollfds,
++ maxfd,readfds,writefds,exceptfds,
++ *now, 0);
++}
++
++/* General helpful functions. */
++
++void adns_globalsystemfailure(adns_state ads) {
++ while (ads->timew.head) {
++ adns__query_fail(ads->timew.head, adns_s_systemfail);
++ }
++
++ switch (ads->tcpstate) {
++ case server_connecting:
++ case server_ok:
++ adns__tcp_closenext(ads);
++ break;
++ case server_disconnected:
++ break;
++ default:
++ abort();
++ }
}
--/* User-visible functions and their implementation. */
++int adns_processany(adns_state ads) {
++ int r;
++ struct timeval now;
++ struct pollfd pollfds[MAX_POLLFDS];
++ int npollfds;
++
++ r= gettimeofday(&now,0);
++ if (!r) adns_processtimeouts(ads,&now);
++
++ npollfds= adns__pollfds(ads,pollfds);
++ adns__fdevents(ads,
++ pollfds,npollfds,
++ 0,0,0,0,
++ now,&r);
++ return r;
++}
void adns__autosys(adns_state ads, struct timeval now) {
if (ads->iflags & adns_if_noautosys) return;
-- adns_callback(ads,-1,0,0,0);
++ adns_processany(ads);
}
static int internal_check(adns_state ads,
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 <stdarg.h>
#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 { 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];
/* From event.c: */
void adns__tcp_broken(adns_state ads, const char *what, const char *why);
++void adns__tcp_closenext(adns_state ads);
void adns__tcp_tryconnect(adns_state ads, struct timeval now);
void adns__autosys(adns_state ads, struct timeval now);
/* Make all the system calls we want to if the application wants us to. */
++void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io,
++ struct timeval *tv_buf);
++void adns__timeouts(adns_state ads, int act,
++ struct timeval **tv_io, struct timeval *tvbuf,
++ struct timeval now);
++int adns__pollfds(adns_state ads, struct pollfd pollfds_buf[MAX_POLLFDS]);
++void adns__fdevents(adns_state ads,
++ const struct pollfd *pollfds, int npollfds,
++ int maxfd, const fd_set *readfds,
++ const fd_set *writefds, const fd_set *exceptfds,
++ struct timeval now, int *r_r);
++
/* Useful static inline functions: */
static inline void timevaladd(struct timeval *tv_io, long ms) {
static inline int ctype_whitespace(int c) { return c==' ' || c=='\n' || c=='\t'; }
static inline int ctype_digit(int c) { return c>='0' && c<='9'; }
static inline int ctype_alpha(int c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' || c <= 'Z');
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
++static inline int errno_resources(int e) { return e==ENOMEM || e==ENOBUFS; }
++
/* Useful macros */
#define MEM_ROUND(sz) \
--- /dev/null
--- /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