Author: Alex Badea <vampire@p16.pub.ro>
authorAlex Badea <vampire@p16.pub.ro>
Sun, 16 Jun 2002 22:59:33 +0000 (22:59 +0000)
committerAlex Badea <vampire@p16.pub.ro>
Sun, 16 Jun 2002 22:59:33 +0000 (22:59 +0000)
Log message:

Implemented adns. I've got it up to the point where it works right under
small load and doesn't core. Hammer it for a while and see how it handles.
Added --disable-adns to configure in case you want the old res.c back.

git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@773 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

36 files changed:
ChangeLog
Makefile.in
adns/COPYING [new file with mode: 0644]
adns/Makefile [new file with mode: 0644]
adns/Makefile.in [new file with mode: 0644]
adns/README [new file with mode: 0644]
adns/README.html [new file with mode: 0644]
adns/README.ircu [new file with mode: 0644]
adns/aclocal.m4 [new file with mode: 0644]
adns/configure [new file with mode: 0755]
adns/configure.in [new file with mode: 0644]
adns/settings.make.in [new file with mode: 0644]
adns/src/Makefile.in [new file with mode: 0644]
adns/src/adns.h [new file with mode: 0644]
adns/src/adns.make [new file with mode: 0644]
adns/src/check.c [new file with mode: 0644]
adns/src/config.h [new file with mode: 0644]
adns/src/config.h.in [new file with mode: 0644]
adns/src/dlist.h [new file with mode: 0644]
adns/src/event.c [new file with mode: 0644]
adns/src/general.c [new file with mode: 0644]
adns/src/internal.h [new file with mode: 0644]
adns/src/parse.c [new file with mode: 0644]
adns/src/poll.c [new file with mode: 0644]
adns/src/query.c [new file with mode: 0644]
adns/src/reply.c [new file with mode: 0644]
adns/src/setup.c [new file with mode: 0644]
adns/src/transmit.c [new file with mode: 0644]
adns/src/tvarith.h [new file with mode: 0644]
adns/src/types.c [new file with mode: 0644]
configure
configure.in
ircd/Makefile.in
ircd/res.c [deleted file]
ircd/res_adns.c [new file with mode: 0644]
ircd/res_libresolv.c [new file with mode: 0644]

index 48a1420e6c5d22ddb955f6f667216037002afff6..05f192f218100fb16a24711361125c43f38af21b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2002-06-17  Alex Badea  <vampire@p16.pub.ro>
+
+       * adns/*: added a slightly hacked copy of adns
+
+       * configure.in: added a --disable-adns switch if you want
+       to use the old libresolv res.c
+
+       * configure: ran autoconf
+
+       * ircd/res_libresolv.c: renamed from res.c
+
+       * ircd/res_adns.c: added adns resolver
+
 2002-06-17  Alex Badea  <vampire@p16.pub.ro>
 
        * ircd/ircd_parser.y: fixed 'Connect' block processing so now
index 2b3f0faa4a2891163bd3f71cf3e3ddf079d87d1c..41df37b62e7d230eb8bc872c0266f742453f2469 100644 (file)
@@ -25,11 +25,12 @@ VPATH = @srcdir@
 SHELL = @SHPROG@
 RM = @RMPROG@
 AWK = @AWK@
+adns_subdir = @ADNS_SUBDIR@
 @SET_MAKE@
 #### End of system configuration section. ####
 
-SUBDIRS = doc ircd
-IRCD_MAKEFILES = Makefile doc/Makefile ircd/Makefile
+SUBDIRS = doc ${adns_subdir} ircd
+IRCD_MAKEFILES = Makefile doc/Makefile adns/Makefile ircd/Makefile
 
 all: build
 
diff --git a/adns/COPYING b/adns/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    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 of the License, 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/adns/Makefile b/adns/Makefile
new file mode 100644 (file)
index 0000000..931ce18
--- /dev/null
@@ -0,0 +1,67 @@
+# Generated automatically from Makefile.in by configure.
+# Makefile - top-level Makefile
+#  
+#  This file is
+#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+#
+#  It is part of adns, which is
+#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+#    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+# 
+#  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. 
+
+# Remember to change ADNS_VERSION_STRING in client/client.h too, and
+# possibly library soname (MAJOR and MINOR in settings.make.in).
+DISTVERSION=   1.0
+
+srcdir=                .
+
+SUBDIRS= src
+
+all install uninstall clean distclean mostlyclean maintainer-clean distprep:
+       set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
+       $(MAKE) $@-here
+
+all-here install-here uninstall-here distprep-here:    README
+
+clean-here mostlyclean-here:
+               rm -f *~ ./#*# core *.orig *.rej adns-*.tar.gz
+               rm -rf dist_tmp
+
+distclean-here maintainer-clean-here:  clean-here
+               rm -f settings.make config.h config.cache config.log config.status
+
+install-strip:
+       $(MAKE) INSTALL_PROGRAM_FLAGS=-s
+
+dist_tmp=dist_tmp/adns-$(DISTVERSION)
+dist:                  distprep
+       rm -rf dist_tmp*
+       mkdir dist_tmp $(dist_tmp)
+       find \( -name CVS -o -name dist_tmp* \) -prune -o -type d -print | \
+               sed -e 's#.*#mkdir -p $(dist_tmp)/&#' | sh
+       find \( -name CVS -o -name dist_tmp* \) -prune -o -type f -print | \
+               sed -e 's#.*#ln & $(dist_tmp)/&#' | sh
+       $(MAKE) -C dist_tmp/adns-$(DISTVERSION) distclean
+       cd dist_tmp && tar cf ../$(dist_tmp).tar `basename $(dist_tmp)`
+       gzip -9 $(dist_tmp).tar
+       mv $(dist_tmp).tar.gz .
+
+README:                        README.html
+       lynx -dump -number_links -cfg=/dev/null ./README.html >README.tmp
+       mv -f README.tmp README
+
+TAGS info dvi:
+       # do nothing
diff --git a/adns/Makefile.in b/adns/Makefile.in
new file mode 100644 (file)
index 0000000..050b71f
--- /dev/null
@@ -0,0 +1,69 @@
+# Makefile - top-level Makefile
+#  
+#  This file is
+#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+#
+#  It is part of adns, which is
+#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+#    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+# 
+#  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. 
+
+# Remember to change ADNS_VERSION_STRING in client/client.h too, and
+# possibly library soname (MAJOR and MINOR in settings.make.in).
+DISTVERSION=   1.0
+
+srcdir=                @srcdir@
+VPATH=         @srcdir@
+
+SUBDIRS= src
+
+build: all
+
+all install uninstall clean distclean mostlyclean maintainer-clean distprep:
+       set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
+       $(MAKE) $@-here
+
+all-here install-here uninstall-here distprep-here:    README
+
+clean-here mostlyclean-here:
+               rm -f *~ ./#*# core *.orig *.rej adns-*.tar.gz
+               rm -rf dist_tmp
+
+distclean-here maintainer-clean-here:  clean-here
+               rm -f settings.make config.h config.cache config.log config.status
+
+install-strip:
+       $(MAKE) INSTALL_PROGRAM_FLAGS=-s
+
+dist_tmp=dist_tmp/adns-$(DISTVERSION)
+dist:                  distprep
+       rm -rf dist_tmp*
+       mkdir dist_tmp $(dist_tmp)
+       find \( -name CVS -o -name dist_tmp* \) -prune -o -type d -print | \
+               sed -e 's#.*#mkdir -p $(dist_tmp)/&#' | sh
+       find \( -name CVS -o -name dist_tmp* \) -prune -o -type f -print | \
+               sed -e 's#.*#ln & $(dist_tmp)/&#' | sh
+       $(MAKE) -C dist_tmp/adns-$(DISTVERSION) distclean
+       cd dist_tmp && tar cf ../$(dist_tmp).tar `basename $(dist_tmp)`
+       gzip -9 $(dist_tmp).tar
+       mv $(dist_tmp).tar.gz .
+
+README:                        README.html
+       lynx -dump -number_links -cfg=/dev/null ./README.html >README.tmp
+       mv -f README.tmp README
+
+TAGS info dvi:
+       # do nothing
diff --git a/adns/README b/adns/README
new file mode 100644 (file)
index 0000000..e98c114
--- /dev/null
@@ -0,0 +1,167 @@
+
+                                   GNU adns
+                                       
+   Advanced, easy to use, asynchronous-capable DNS client library and
+   utilities.
+   
+   adns is a resolver library for C (and C++) programs, and a collection
+   of useful DNS resolver utilities.
+   
+C library
+
+   In contrast with the standard interfaces, gethostbyname et al and
+   libresolv, it has the following features:
+     * It is reasonably easy to use for simple programs which just want
+       to translate names to addresses, look up MX records, etc.
+     * It can be used in an asynchronous, non-blocking, manner. Many
+       queries can be handled simultaneously.
+     * Responses are decoded automatically into a natural representation
+       for a C program - there is no need to deal with DNS packet
+       formats.
+     * Sanity checking (eg, name syntax checking, reverse/forward
+       correspondence, CNAME pointing to CNAME) is performed
+       automatically.
+     * Time-to-live, CNAME and other similar information is returned in
+       an easy-to-use form, without getting in the way.
+     * There is no global state in the library; resolver state is an
+       opaque data structure which the client creates explicitly. A
+       program can have several instances of the resolver.
+     * Errors are reported to the application in a way that distinguishes
+       the various causes of failure properly.
+     * Understands conventional resolv.conf, but this can overridden by
+       environment variables.
+     * Flexibility. For example, the application can tell adns to: ignore
+       environment variables (for setuid programs), disable hostname
+       syntax sanity checks to return arbitrary data, override or ignore
+       resolv.conf in favour of supplied configuration, etc.
+     * Believed to be correct ! For example, will correctly back off to
+       TCP in case of long replies or queries, or to other nameservers if
+       several are available. It has sensible handling of bad responses
+       etc.
+       
+DNS utility programs
+
+   adns also comes with a number of utility programs for use from the
+   command line and in scripts:
+     * adnslogres is a much faster version of Apache's logresolv program.
+     * adnsresfilter is a filter which copies its input to its output,
+       replacing IP addresses by the corresponding names, without unduly
+       delaying the output. For example, you can usefully pipe the output
+       of netstat -n, tcpdump -ln, and the like, into it.
+     * adnshost is a general-purpose DNS lookup utility which can be used
+       easily in from the command line and from shell scripts to do
+       simple lookups. In a more advanced mode it can be used as a
+       general-purpose DNS helper program for scripting languages which
+       can invoke and communicate with subprocesses. See the [1]adnshost
+       usage message for a summary of its capabilities.
+       
+Documentation
+
+   I'm afraid there is no manual yet. However, competent C programmers
+   should be able to use the library based on the [2]commented adns.h
+   header file, and the usage messages for the programs should be
+   sufficient.
+   
+Feedback
+
+   I'd be pleased if you would let me know if you're using my library in
+   your project, and what you think of it.
+   
+   If you are subscribed to adns-discuss please send feedback, including
+   bug reports, there; otherwise send mail to
+   adns-bugreports@chiark.greenend.org.uk. If you'd prefer that your
+   message wasn't forwarded to the adns-bugreports list, send it to
+   adns-maint@chiark.greenend.org.uk.
+   
+Mailinglists
+
+   I have set up mailinglists adns-announce and adns-discuss. The
+   announcements list is moderated and will contain only announcements of
+   important bugs, new versions, etc. The bug reports address mentioned
+   above is also a mailing list; feel free to subscribe to it.
+   
+   There are [3]archives and subscription web pages, or you can subscribe
+   by sending mail containing the word `subscribe' to
+   adns-announce-REQUEST@chiark.greenend.org.uk or
+   adns-discuss-REQUEST@chiark.greenend.org.uk.
+   
+Download
+
+   Available for download from [4]chiark.greenend.org.uk are:
+     * The [5]current release as a gzipped tarfile.
+     * [6]adns.h API header file with comments, and [7]usage message for
+       adnshost (currently there is no manual, sorry).
+     * All versions released so far are also available via [8]anonymous
+       FTP and [9]HTTP,
+     * A mirror of my CVS repository is available via rsync from
+       rsync.chiark.greenend.org.uk::ftp/users/ian/cvs-pub/adns (use FTP
+       first to find your way around), or via [10]cvsweb.
+       
+   adns is also available from the [11]GNU Project FTP servers and their
+   [12]mirrors.
+   
+Technical note
+
+   adns requires a real nameserver like [13]BIND or [14]Dents running on
+   the same system or a nearby one, which must be willing to provide
+   `recursive service'. I.e., adns is a `stub resolver'. All properly
+   configured UN*X and GNU systems will already have such nameserver(s);
+   they are usually listed in /etc/resolv.conf.
+   
+Copyright and licensing
+
+   adns is Copyright 1997-2000 Ian Jackson, Copyright 1999-2000 Tony
+   Finch, and Copyright (C) 1991 Massachusetts Institute of Technology.
+   
+   adns 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 of the License, or (at your
+   option) any later version.
+   
+   This program and documentation 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
+   [15]GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with adns, or one should be available above; if not, write to
+   the [16]Free Software Foundation, 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA, or email adns-maint@chiark.greenend.org.uk.
+     _________________________________________________________________
+   
+   [17]Ian Jackson / [18]adns-maint@chiark.greenend.org.uk; more [19]free
+   software by me.
+   
+   [20]GNU home page; [21]chiark home page; [22]site or mirror home page
+   
+   This web page is Copyright (C)1996-2000 Ian Jackson. See the
+   [23]Copyright/acknowledgements.
+   
+   Use any browser - [24]Campaign for a non-browser-specific WWW
+
+References
+
+   1. http://www.chiark.greenend.org.uk/~ian/adns/adnshost.txt
+   2. http://www.chiark.greenend.org.uk/~ian/adns/adns.h.txt
+   3. http://www.chiark.greenend.org.uk/mailman/listinfo
+   4. http://www.chiark.greenend.org.uk/~ian/adns/
+   5. http://www.chiark.greenend.org.uk/~ian/adns/adns.tar.gz
+   6. http://www.chiark.greenend.org.uk/~ian/adns/adns.h.txt
+   7. http://www.chiark.greenend.org.uk/~ian/adns/adnshost.txt
+   8. ftp://ftp.chiark.greenend.org.uk/users/ian/adns/
+   9. http://www.chiark.greenend.org.uk/~ian/adns/ftp/
+  10. http://www.chiark.greenend.org.uk/ucgi/~ijackson/cvsweb/adns/
+  11. http://www.gnu.org/
+  12. http://www.gnu.org/order/ftp.html
+  13. http://www.isc.org/view.cgi?/products/BIND/index.phtml
+  14. http://www.dents.org/
+  15. http://www.chiark.greenend.org.uk/~ian/COPYING.txt
+  16. http://www.fsf.org/
+  17. http://www.chiark.greenend.org.uk/
+  18. mailto:adns-maint@chiark.greenend.org.uk
+  19. http://www.chiark.greenend.org.uk/~ian/software/
+  20. http://www.gnu.org/
+  21. http://www.chiark.greenend.org.uk/
+  22. file://localhost/
+  23. http://www.chiark.greenend.org.uk/~ian/sw-www-copy.html
+  24. http://www.anybrowser.org/campaign/
diff --git a/adns/README.html b/adns/README.html
new file mode 100644 (file)
index 0000000..b01a1a1
--- /dev/null
@@ -0,0 +1,222 @@
+<html>
+<head>
+<title>adns - advanced, alternative, asynchronous resolver</title>
+<link rev="made" href="mailto:adns-maint@chiark.greenend.org.uk">
+</head>
+<body>
+<h1>GNU adns</h1>
+
+<strong>Advanced, easy to use, asynchronous-capable DNS client
+library and utilities.</strong>
+
+<!-- Note: this file is maintained in Ian Jackson's private CVS.  -->
+<!--                                                              -->
+<!--  It is served on the GNU site and also from my own system,   -->
+<!--  under the URL http://www.chiark.greenend.org.uk/adns/       -->
+<!--  Please ensure that all links continine to be correct        -->
+<!--  both for www.gnu.org and chiark.                            -->
+<!--                                                              -->
+<!--  $Id: README.html,v 1.1 2002-06-16 22:59:32 vampire Exp $        -->   
+
+<p>
+
+adns is a resolver library for C (and C++) programs, and a collection
+of useful DNS resolver utilities.
+
+
+<h2>C library</h2>
+
+In contrast with the standard interfaces, gethostbyname et al and
+libresolv, it has the following features:
+
+<ul>
+
+<li>It is reasonably easy to use for simple programs which just want
+to translate names to addresses, look up MX records, etc.
+
+<li>It can be used in an asynchronous, non-blocking, manner.  Many
+queries can be handled simultaneously.
+
+<li>Responses are decoded automatically into a natural representation
+for a C program - there is no need to deal with DNS packet formats.
+
+<li>Sanity checking (eg, name syntax checking, reverse/forward
+correspondence, CNAME pointing to CNAME) is performed automatically.
+
+<li>Time-to-live, CNAME and other similar information is returned in
+an easy-to-use form, without getting in the way.
+
+<li>There is no global state in the library; resolver state is an
+opaque data structure which the client creates explicitly.  A program
+can have several instances of the resolver.
+
+<li>Errors are reported to the application in a way that distinguishes
+the various causes of failure properly.
+
+<li>Understands conventional resolv.conf, but this can overridden by
+environment variables.
+
+<li>Flexibility.  For example, the application can tell adns to:
+ignore environment variables (for setuid programs), disable hostname
+syntax sanity checks to return arbitrary data, override or ignore
+resolv.conf in favour of supplied configuration, etc.
+
+<li>Believed to be correct !  For example, will correctly back off to
+TCP in case of long replies or queries, or to other nameservers if
+several are available.  It has sensible handling of bad responses etc.
+
+</ul>
+
+<h2>DNS utility programs</h2>
+
+adns also comes with a number of utility programs for use from the
+command line and in scripts:
+
+<ul>
+
+<li><code>adnslogres</code> is a much faster version of Apache's
+logresolv program.
+
+<li><code>adnsresfilter</code> is a filter which copies its input to
+its output, replacing IP addresses by the corresponding names, without
+unduly delaying the output.  For example, you can usefully pipe the
+output of netstat -n, tcpdump -ln, and the like, into it.
+
+<li><code>adnshost</code> is a general-purpose DNS lookup utility
+which can be used easily in from the command line and from shell
+scripts to do simple lookups.  In a more advanced mode it can be used
+as a general-purpose DNS helper program for scripting languages which
+can invoke and communicate with subprocesses.  See the
+<A href="http://www.chiark.greenend.org.uk/~ian/adns/adnshost.txt">adnshost
+usage message</A> for a summary of its capabilities.
+
+</ul>
+
+<h2>Documentation</h2>
+
+I'm afraid there is no manual yet.  However, competent C programmers
+should be able to use the library based on the
+<A href="http://www.chiark.greenend.org.uk/~ian/adns/adns.h.txt">commented
+adns.h header file</A>, and the usage messages for the programs should
+be sufficient.
+
+<h2>Feedback</h2>
+
+I'd be pleased if you would let me know if you're using my library in
+your project, and what you think of it.
+
+<p>
+
+If you are subscribed to <code>adns-discuss</code> please send
+feedback, including bug reports, there; otherwise send mail to
+<code>adns-bugreports@chiark.greenend.org.uk</code>.  If you'd prefer
+that your message wasn't forwarded to the <code>adns-bugreports</code>
+list, send it to <code>adns-maint@chiark.greenend.org.uk</code>.
+
+<h2>Mailinglists</h2>
+
+I have set up mailinglists <code>adns-announce</code> and
+<code>adns-discuss</code>.  The announcements list is moderated and
+will contain only announcements of important bugs, new versions, etc.
+The bug reports address mentioned above is also a mailing list; feel
+free to subscribe to it.
+
+<p>
+
+There are
+<A href="http://www.chiark.greenend.org.uk/mailman/listinfo">archives
+and subscription web pages</A>, or you can subscribe by sending mail
+containing the word `subscribe' to
+<code>adns-announce-REQUEST@chiark.greenend.org.uk</code> or
+<code>adns-discuss-REQUEST@chiark.greenend.org.uk</code>.
+
+<h2>Download</h2>
+
+Available for download from
+<A href="http://www.chiark.greenend.org.uk/~ian/adns/">chiark.greenend.org.uk</A>
+are:
+<ul>
+<li>The <A href="http://www.chiark.greenend.org.uk/~ian/adns/adns.tar.gz">current
+    release</A> as a gzipped tarfile.
+<li><A href="http://www.chiark.greenend.org.uk/~ian/adns/adns.h.txt">adns.h</A>
+    API header file with comments, and
+    <A href="http://www.chiark.greenend.org.uk/~ian/adns/adnshost.txt">usage
+    message for adnshost</A> (currently there is no manual, sorry).
+<li>All versions released so far are also available via
+    <A href="ftp://ftp.chiark.greenend.org.uk/users/ian/adns/">anonymous
+    FTP</A> and <A href="http://www.chiark.greenend.org.uk/~ian/adns/ftp/">HTTP</A>,
+<li>A mirror of my CVS repository is available via rsync from
+    <code>rsync.chiark.greenend.org.uk::ftp/users/ian/cvs-pub/adns</code>
+    (use FTP first to find your way around), or via
+    <A href="http://www.chiark.greenend.org.uk/ucgi/~ijackson/cvsweb/adns/">cvsweb</A>.
+</ul>
+
+adns is also available from the
+<A href="http://www.gnu.org/">GNU Project</A> FTP servers and their
+<A href="http://www.gnu.org/order/ftp.html">mirrors</A>.
+
+<h2>Technical note</h2>
+
+adns requires a real nameserver like
+<A href="http://www.isc.org/view.cgi?/products/BIND/index.phtml">BIND</A>
+or
+<A href="http://www.dents.org/">Dents</A> running on
+the same system or a nearby one, which must be willing to provide
+`recursive service'.  I.e., adns is a `stub resolver'.  All properly
+configured UN*X and GNU systems will already have such nameserver(s);
+they are usually listed in /etc/resolv.conf.
+
+<h2>Copyright and licensing</h2>
+
+<kbd>adns</kbd> is Copyright 1997-2000 Ian Jackson, Copyright
+1999-2000 Tony Finch, and Copyright (C) 1991 Massachusetts Institute
+of Technology.
+
+<p>
+
+<kbd>adns</kbd> 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 of the License, or (at
+your option) any later version.
+
+<p>
+
+This program and documentation is distributed in the hope that it will
+be useful, but <em>without any warranty</em>; without even the implied
+warranty of <em>merchantability</em> or <em>fitness for a particular
+purpose</em>.  See the
+<A href="http://www.chiark.greenend.org.uk/~ian/COPYING.txt">GNU
+General Public License</A> for more details.
+
+<p>
+
+You should have received a copy of the GNU General Public License
+along with <kbd>adns</kbd>, or one should be available above; if not,
+write to the
+<A href="http://www.fsf.org/">Free Software Foundation</A>,
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA,
+or email <code>adns-maint@chiark.greenend.org.uk</code>.
+
+<p>
+
+<hr>
+<A href="http://www.chiark.greenend.org.uk/">Ian Jackson</A> / 
+<A href="mailto:adns-maint@chiark.greenend.org.uk"><tt>adns-maint@chiark.greenend.org.uk</tt></A>;
+more <A href="http://www.chiark.greenend.org.uk/~ian/software/">free
+software</A> by me.
+<p>
+
+<A href="http://www.gnu.org/">GNU home page</A>;
+<A href="http://www.chiark.greenend.org.uk/">chiark home page</A>;
+<A href="/">site or mirror home page</A>
+<p>
+
+This web page is Copyright (C)1996-2000 Ian Jackson.  See the
+<A href="http://www.chiark.greenend.org.uk/~ian/sw-www-copy.html">Copyright/acknowledgements</A>.
+<p>
+
+Use any browser -
+<A href="http://www.anybrowser.org/campaign/">Campaign for a non-browser-specific WWW</A>
+
+</body>
+</html>
diff --git a/adns/README.ircu b/adns/README.ircu
new file mode 100644 (file)
index 0000000..cebc00d
--- /dev/null
@@ -0,0 +1,3 @@
+The files in adns/ were borrowed from the ADNS library and slightly 
+modified to work with ircu's event loop. The original code's copyright
+notices are in the README file.
diff --git a/adns/aclocal.m4 b/adns/aclocal.m4
new file mode 100644 (file)
index 0000000..6c4099e
--- /dev/null
@@ -0,0 +1,70 @@
+# aclocal.m4 - package-specific macros for autoconf
+#  
+#  This file is
+#    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+#
+#  It is part of adns, which is
+#    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+#    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+#  
+#  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. 
+
+dnl DPKG_CACHED_TRY_COMPILE(<description>,<cachevar>,<include>,<program>,<ifyes>,<ifno>)
+define(DPKG_CACHED_TRY_COMPILE,[
+ AC_MSG_CHECKING($1)
+ AC_CACHE_VAL($2,[
+  AC_TRY_COMPILE([$3],[$4],[$2=yes],[$2=no])
+ ])
+ if test "x$$2" = xyes; then
+  true
+  $5
+ else
+  true
+  $6
+ fi
+])
+
+define(ADNS_C_GCCATTRIB,[
+ DPKG_CACHED_TRY_COMPILE(__attribute__((,,)),adns_cv_c_attribute_supported,,
+  [extern int testfunction(int x) __attribute__((,,))],
+  AC_MSG_RESULT(yes)
+  AC_DEFINE(HAVE_GNUC25_ATTRIB)
+   DPKG_CACHED_TRY_COMPILE(__attribute__((noreturn)),adns_cv_c_attribute_noreturn,,
+    [extern int testfunction(int x) __attribute__((noreturn))],
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(HAVE_GNUC25_NORETURN),
+    AC_MSG_RESULT(no))
+   DPKG_CACHED_TRY_COMPILE(__attribute__((const)),adns_cv_c_attribute_const,,
+    [extern int testfunction(int x) __attribute__((const))],
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(HAVE_GNUC25_CONST),
+    AC_MSG_RESULT(no))
+   DPKG_CACHED_TRY_COMPILE(__attribute__((format...)),adns_cv_attribute_format,,
+    [extern int testfunction(char *y, ...) __attribute__((format(printf,1,2)))],
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(HAVE_GNUC25_PRINTFFORMAT),
+    AC_MSG_RESULT(no)),
+  AC_MSG_RESULT(no))
+])
+
+define(ADNS_C_GETFUNC,[
+ AC_CHECK_FUNC([$1],,[
+  AC_CHECK_LIB([$2],[$1],[$3],[
+    AC_MSG_ERROR([cannot find library function $1])
+  ])
+ ])
+])
diff --git a/adns/configure b/adns/configure
new file mode 100755 (executable)
index 0000000..31ddc81
--- /dev/null
@@ -0,0 +1,1994 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.13"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=src/adns.h
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:532: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:562: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+       continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:613: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:645: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 656 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:661: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:687: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:692: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:701: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:720: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:752: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 767 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:773: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 784 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:790: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -nologo -E"
+  cat > conftest.$ac_ext <<EOF
+#line 801 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:807: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+# Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:834: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_RANLIB="ranlib"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+  echo "$ac_t""$RANLIB" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:892: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    IFS="${IFS=        }"; ac_save_IFS="$IFS"; IFS=":"
+  for ac_dir in $PATH; do
+    # Account for people who put trailing slashes in PATH elements.
+    case "$ac_dir/" in
+    /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+    *)
+      # OSF1 and SCO ODT 3.0 have their own names for install.
+      # Don't use installbsd from OSF since it installs stuff as root
+      # by default.
+      for ac_prog in ginstall scoinst install; do
+        if test -f $ac_dir/$ac_prog; then
+         if test $ac_prog = install &&
+            grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         else
+           ac_cv_path_install="$ac_dir/$ac_prog -c"
+           break 2
+         fi
+       fi
+      done
+      ;;
+    esac
+  done
+  IFS="$ac_save_IFS"
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL="$ac_cv_path_install"
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL="$ac_install_sh"
+  fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+for ac_func in poll
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:948: 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 953 "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:976: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; 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:1002: 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 1007 "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
+if { (eval echo configure:1030: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_socket=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_socket=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'socket`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+
+  echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
+echo "configure:1049: 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
+#line 1057 "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
+if { (eval echo configure:1068: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; 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
+echo "configure:1104: 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 1109 "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
+if { (eval echo configure:1132: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_inet_ntoa=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_inet_ntoa=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'inet_ntoa`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+
+  echo $ac_n "checking for inet_ntoa in -lnsl""... $ac_c" 1>&6
+echo "configure:1151: 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
+#line 1159 "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
+if { (eval echo configure:1170: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; 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 INADDR_LOOPBACK""... $ac_c" 1>&6
+echo "configure:1206: checking for INADDR_LOOPBACK" >&5
+if eval "test \"`echo '$''{'adns_cv_decl_inaddrloopback'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  
+ cat > conftest.$ac_ext <<EOF
+#line 1212 "configure"
+#include "confdefs.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+int main() {
+
+  INADDR_LOOPBACK;
+; return 0; }
+EOF
+if { (eval echo configure:1225: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  adns_cv_decl_inaddrloopback=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  adns_cv_decl_inaddrloopback=no
+fi
+rm -f conftest*
+fi
+
+if test "$adns_cv_decl_inaddrloopback" = yes; then
+ echo "$ac_t""found" 1>&6
+else
+ echo "$ac_t""not in standard headers, urgh..." 1>&6
+ ac_safe=`echo "rpc/types.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for rpc/types.h""... $ac_c" 1>&6
+echo "configure:1243: checking for rpc/types.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1248 "configure"
+#include "confdefs.h"
+#include <rpc/types.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1253: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  
+  cat >> confdefs.h <<\EOF
+#define HAVEUSE_RPCTYPES_H 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+
+  { echo "configure: error: cannot find INADDR_LOOPBACK or rpc/types.h" 1>&2; exit 1; }
+fi
+
+fi
+
+
+ echo $ac_n "checking for inet_aton""... $ac_c" 1>&6
+echo "configure:1286: 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 1291 "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
+if { (eval echo configure:1314: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_inet_aton=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_inet_aton=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'inet_aton`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  :
+else
+  echo "$ac_t""no" 1>&6
+
+  echo $ac_n "checking for inet_aton in -lresolv""... $ac_c" 1>&6
+echo "configure:1333: 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
+#line 1341 "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:1352: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; 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 inlines""... $ac_c" 1>&6
+echo "configure:1385: checking inlines" >&5
+ if eval "test \"`echo '$''{'dpkg_cv_c_inline'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  
+  cat > conftest.$ac_ext <<EOF
+#line 1391 "configure"
+#include "confdefs.h"
+
+int main() {
+} inline int foo (int x) {
+; return 0; }
+EOF
+if { (eval echo configure:1398: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  dpkg_cv_c_inline=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  dpkg_cv_c_inline=no
+fi
+rm -f conftest*
+fi
+
+ if test "x$dpkg_cv_c_inline" = xyes; then
+  true
+  echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_INLINE 1
+EOF
+
+ else
+  true
+  echo "$ac_t""no" 1>&6
+ fi
+
+
+
+ echo $ac_n "checking __attribute__((,,))""... $ac_c" 1>&6
+echo "configure:1427: 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 1433 "configure"
+#include "confdefs.h"
+
+int main() {
+extern int testfunction(int x) __attribute__((,,))
+; return 0; }
+EOF
+if { (eval echo configure:1440: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  adns_cv_c_attribute_supported=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  adns_cv_c_attribute_supported=no
+fi
+rm -f conftest*
+fi
+
+ if test "x$adns_cv_c_attribute_supported" = xyes; then
+  true
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_GNUC25_ATTRIB 1
+EOF
+
+   
+ echo $ac_n "checking __attribute__((noreturn))""... $ac_c" 1>&6
+echo "configure:1462: 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 1468 "configure"
+#include "confdefs.h"
+
+int main() {
+extern int testfunction(int x) __attribute__((noreturn))
+; return 0; }
+EOF
+if { (eval echo configure:1475: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  adns_cv_c_attribute_noreturn=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  adns_cv_c_attribute_noreturn=no
+fi
+rm -f conftest*
+fi
+
+ if test "x$adns_cv_c_attribute_noreturn" = xyes; then
+  true
+  echo "$ac_t""yes" 1>&6
+    cat >> confdefs.h <<\EOF
+#define HAVE_GNUC25_NORETURN 1
+EOF
+
+ else
+  true
+  echo "$ac_t""no" 1>&6
+ fi
+
+   
+ echo $ac_n "checking __attribute__((const))""... $ac_c" 1>&6
+echo "configure:1502: 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 1508 "configure"
+#include "confdefs.h"
+
+int main() {
+extern int testfunction(int x) __attribute__((const))
+; return 0; }
+EOF
+if { (eval echo configure:1515: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  adns_cv_c_attribute_const=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  adns_cv_c_attribute_const=no
+fi
+rm -f conftest*
+fi
+
+ if test "x$adns_cv_c_attribute_const" = xyes; then
+  true
+  echo "$ac_t""yes" 1>&6
+    cat >> confdefs.h <<\EOF
+#define HAVE_GNUC25_CONST 1
+EOF
+
+ else
+  true
+  echo "$ac_t""no" 1>&6
+ fi
+
+   
+ echo $ac_n "checking __attribute__((format...))""... $ac_c" 1>&6
+echo "configure:1542: 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 1548 "configure"
+#include "confdefs.h"
+
+int main() {
+extern int testfunction(char *y, ...) __attribute__((format(printf,1,2)))
+; return 0; }
+EOF
+if { (eval echo configure:1555: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  adns_cv_attribute_format=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  adns_cv_attribute_format=no
+fi
+rm -f conftest*
+fi
+
+ if test "x$adns_cv_attribute_format" = xyes; then
+  true
+  echo "$ac_t""yes" 1>&6
+    cat >> confdefs.h <<\EOF
+#define HAVE_GNUC25_PRINTFFORMAT 1
+EOF
+
+ else
+  true
+  echo "$ac_t""no" 1>&6
+ fi
+
+ else
+  true
+  echo "$ac_t""no" 1>&6
+ fi
+
+
+for ac_hdr in sys/select.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1590: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1595 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1600: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+
+
+if test "${GCC-no}" = yes; then
+       WARNS="-Wall -Wmissing-prototypes -Wwrite-strings -Wstrict-prototypes -Wcast-qual -Wpointer-arith"
+else
+       WARNS=
+fi
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "settings.make Makefile src/Makefile
+ src/config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+s%@RANLIB@%$RANLIB%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@WARNS@%$WARNS%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"settings.make Makefile src/Makefile
+"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  case "$ac_given_INSTALL" in
+  [/$]*) INSTALL="$ac_given_INSTALL" ;;
+  *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+  esac
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='\([     ][      ]*\)[^  ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='\([     ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="src/config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/adns/configure.in b/adns/configure.in
new file mode 100644 (file)
index 0000000..014c270
--- /dev/null
@@ -0,0 +1,97 @@
+# configure.in - input to autoconf
+#  
+#  This file is
+#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+#
+#  It is part of adns, which is
+#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+#    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+#  
+#  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. 
+
+AC_INIT(src/adns.h)
+AC_CONFIG_HEADER(src/config.h)
+
+dnl DPKG_CACHED_TRY_COMPILE(<description>,<cachevar>,<include>,<program>,<ifyes>,<ifno>)
+define(DPKG_CACHED_TRY_COMPILE,[
+ AC_MSG_CHECKING($1)
+ AC_CACHE_VAL($2,[
+  AC_TRY_COMPILE([$3],[$4],[$2=yes],[$2=no])
+ ])
+ if test "x$$2" = xyes; then
+  true
+  $5
+ else
+  true
+  $6
+ fi
+])
+
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_RANLIB
+AC_PROG_INSTALL
+
+AC_CHECK_FUNCS(poll)
+ADNS_C_GETFUNC(socket,socket)
+ADNS_C_GETFUNC(inet_ntoa,nsl)
+
+AC_MSG_CHECKING(for INADDR_LOOPBACK)
+AC_CACHE_VAL(adns_cv_decl_inaddrloopback,[
+ AC_TRY_COMPILE([
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+ ],[
+  INADDR_LOOPBACK;
+ ],
+ adns_cv_decl_inaddrloopback=yes,
+ adns_cv_decl_inaddrloopback=no)])
+if test "$adns_cv_decl_inaddrloopback" = yes; then
+ AC_MSG_RESULT(found)
+else
+ AC_MSG_RESULT([not in standard headers, urgh...])
+ AC_CHECK_HEADER(rpc/types.h,[
+  AC_DEFINE(HAVEUSE_RPCTYPES_H)
+ ],[
+  AC_MSG_ERROR([cannot find INADDR_LOOPBACK or rpc/types.h])
+ ])
+fi
+
+ADNS_C_GETFUNC(inet_aton,resolv,[
+ LIBS="-lresolv $LIBS";
+ AC_MSG_WARN([inet_aton is in libresolv, urgh.  Must use -lresolv.])
+])
+
+DPKG_CACHED_TRY_COMPILE(inlines,dpkg_cv_c_inline,,
+ [} inline int foo (int x) {],
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_INLINE),
+ AC_MSG_RESULT(no))
+
+ADNS_C_GCCATTRIB
+AC_CHECK_HEADERS(sys/select.h)
+
+AC_SUBST(WARNS)
+
+if test "${GCC-no}" = yes; then
+       WARNS="-Wall -Wmissing-prototypes -Wwrite-strings -Wstrict-prototypes -Wcast-qual -Wpointer-arith"
+else
+       WARNS=
+fi
+
+AC_OUTPUT(
+       settings.make Makefile src/Makefile
+)
diff --git a/adns/settings.make.in b/adns/settings.make.in
new file mode 100644 (file)
index 0000000..13de1fc
--- /dev/null
@@ -0,0 +1,64 @@
+# settings.make.in - main configuration settings for Makefiles,
+#  used by autoconf/configure to generate settings.make
+#  
+#  This file is
+#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+#
+#  It is part of adns, which is
+#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+#    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+#  
+#  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. 
+
+CC=            @CC@
+CFLAGS=                $(AUTOCFLAGS) $(WARNS) $(WERROR) $(DIRCFLAGS) $(XCFLAGS)
+LDFLAGS=       $(AUTOLDFLAGS) $(DIRLDFLAGS) $(XLDFLAGS)
+LDLIBS=                @LIBS@ $(XLIBS)
+AUTOCFLAGS=    @CFLAGS@
+AUTOLDFLAGS=   @LDFLAGS@
+WARNS=         @WARNS@
+#WERROR=       -Werror
+
+M4=            m4
+RANLIB=                @RANLIB@
+
+prefix=                @prefix@
+exec_prefix=   @exec_prefix@
+bin_dir=       $(exec_prefix)/bin
+lib_dir=       $(exec_prefix)/lib
+include_dir=   $(prefix)/include
+
+AC_INSTALL=    @INSTALL@
+ifeq ($(AC_INSTALL),./install-sh -c)
+INSTALL=       $(srcdir)/../$(AC_INSTALL)
+else
+INSTALL=       $(AC_INSTALL)
+endif
+
+INSTALL_PROGRAM=       $(INSTALL) -m 755 $(INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA=          $(INSTALL) -m 644
+
+all:                   $(TARGETS)
+
+clean mostlyclean:
+               rm -f *.o *.tmp* *.so *.so.*
+
+distclean:             clean
+               rm -f $(TARGETS) *~ ./#*# core *.orig *.rej Makefile
+
+distprep:              $(AUTOCSRCS) $(AUTOCHDRS)
+
+maintainer-clean:      distclean
+               -rm -f $(AUTOCSRCS) $(AUTOCHDRS)
diff --git a/adns/src/Makefile.in b/adns/src/Makefile.in
new file mode 100644 (file)
index 0000000..d19f303
--- /dev/null
@@ -0,0 +1,48 @@
+# src/Makefile - library main Makefile
+# 
+#  This file is
+#    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+#
+#  It is part of adns, which is
+#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+#    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+#  
+#  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. 
+
+srcdir=                @srcdir@
+VPATH=         @srcdir@
+
+TARGETS=       libadns.a
+include                ../settings.make
+include                $(srcdir)/adns.make
+
+install:
+               set -xe; for f in $(TARGETS); \
+                       do $(INSTALL_DATA) $$f $(lib_dir)/$$f; done
+               $(INSTALL_DATA) $(srcdir)/../src/adns.h $(include_dir)/adns.h
+
+uninstall:
+               for f in $(TARGETS); do rm -f $(lib_dir)/$$f; done
+               rm -f $(include_dir)/adns.h
+
+ALLOBJS=       $(LIBOBJS)
+
+libadns.a:     $(LIBOBJS)
+               rm -f $@
+               $(AR) cqv $@.new $(LIBOBJS)
+               $(RANLIB) $@.new
+               mv -f $@.new $@
+
+$(LIBOBJS):    adns.h internal.h config.h
diff --git a/adns/src/adns.h b/adns/src/adns.h
new file mode 100644 (file)
index 0000000..687a927
--- /dev/null
@@ -0,0 +1,858 @@
+/*
+ * adns.h
+ * - adns user-visible API (single-threaded, without any locking)
+ */
+/*
+ *
+ *  This file is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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.
+ *
+ * 
+ *  For the benefit of certain LGPL'd `omnibus' software which
+ *  provides a uniform interface to various things including adns, I
+ *  make the following additional licence.  I do this because the GPL
+ *  would otherwise force either the omnibus software to be GPL'd or
+ *  the adns-using part to be distributed separately.
+ *  
+ *  So: you may also redistribute and/or modify adns.h (but only the
+ *  public header file adns.h and not any other part of adns) under the
+ *  terms of the GNU Library General Public License as published by the
+ *  Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *  
+ *  Note that adns itself is GPL'd.  Authors of adns-using applications
+ *  with GPL-incompatible licences, and people who distribute adns with
+ *  applications where the whole distribution is not GPL'd, are still
+ *  likely to be in violation of the GPL.  Anyone who wants to do this
+ *  should contact Ian Jackson.  Please note that to avoid encouraging
+ *  people to infringe the GPL as it applies to the body of adns, Ian
+ *  thinks that if you take advantage of the special exception to
+ *  redistribute just adns.h under the LGPL, you should retain this
+ *  paragraph in its place in the appropriate copyright statements.
+ *
+ *
+ *  You should have received a copy of the GNU General Public License,
+ *  or the GNU Library General Public License, as appropriate, along
+ *  with this program; if not, write to the Free Software Foundation,
+ *  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ *  $Id$
+ */
+
+#ifndef ADNS_H_INCLUDED
+#define ADNS_H_INCLUDED
+
+#include <stdio.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" { /* I really dislike this - iwj. */
+#endif
+
+/* All struct in_addr anywhere in adns are in NETWORK byte order. */
+
+typedef struct adns__state *adns_state;
+typedef struct adns__query *adns_query;
+
+typedef enum {
+  adns_if_noenv=        0x0001, /* do not look at environment */
+  adns_if_noerrprint=   0x0002, /* never print output to stderr (_debug overrides) */
+  adns_if_noserverwarn= 0x0004, /* do not warn to stderr about duff nameservers etc */
+  adns_if_debug=        0x0008, /* enable all output to stderr plus debug msgs */
+  adns_if_logpid=       0x0080, /* include pid in diagnostic output */
+  adns_if_noautosys=    0x0010, /* do not make syscalls at every opportunity */
+  adns_if_eintr=        0x0020, /* allow _wait and _synchronous to return EINTR */
+  adns_if_nosigpipe=    0x0040, /* applic has SIGPIPE set to SIG_IGN, do not protect */
+  adns_if_checkc_entex= 0x0100, /* do consistency checks on entry/exit to adns funcs */
+  adns_if_checkc_freq=  0x0300  /* do consistency checks very frequently (slow!) */
+} adns_initflags;
+
+typedef enum {
+  adns_qf_search=          0x00000001, /* use the searchlist */
+  adns_qf_usevc=           0x00000002, /* use a virtual circuit (TCP connection) */
+  adns_qf_owner=           0x00000004, /* fill in the owner field in the answer */
+  adns_qf_quoteok_query=   0x00000010, /* allow special chars in query domain */
+  adns_qf_quoteok_cname=   0x00000000, /* allow ... in CNAME we go via - now default */
+  adns_qf_quoteok_anshost= 0x00000040, /* allow ... in things supposed to be hostnames */
+  adns_qf_quotefail_cname= 0x00000080, /* refuse if quote-req chars in CNAME we go via */
+  adns_qf_cname_loose=     0x00000100, /* allow refs to CNAMEs - without, get _s_cname */
+  adns_qf_cname_forbid=    0x00000200, /* don't follow CNAMEs, instead give _s_cname */
+  adns__qf_internalmask=   0x0ff00000
+} adns_queryflags;
+
+typedef enum {
+  adns__rrt_typemask=  0x0ffff,
+  adns__qtf_deref=     0x10000, /* dereference domains and perhaps produce extra data */
+  adns__qtf_mail822=   0x20000, /* make mailboxes be in RFC822 rcpt field format */
+  
+  adns_r_none=               0,
+  
+  adns_r_a=                  1,
+  
+  adns_r_ns_raw=             2,
+  adns_r_ns=                    adns_r_ns_raw|adns__qtf_deref,
+  
+  adns_r_cname=              5,
+  
+  adns_r_soa_raw=            6,
+  adns_r_soa=                   adns_r_soa_raw|adns__qtf_mail822, 
+  
+  adns_r_ptr_raw=           12,
+  adns_r_ptr=                   adns_r_ptr_raw|adns__qtf_deref,
+  
+  adns_r_hinfo=             13,  
+  
+  adns_r_mx_raw=            15,
+  adns_r_mx=                    adns_r_mx_raw|adns__qtf_deref,
+  
+  adns_r_txt=               16,
+  
+  adns_r_rp_raw=            17,
+  adns_r_rp=                    adns_r_rp_raw|adns__qtf_mail822,
+
+  adns_r_addr=                  adns_r_a|adns__qtf_deref
+  
+} adns_rrtype;
+
+/*
+ * In queries without qf_quoteok_*, all domains must have standard
+ * legal syntax, or you get adns_s_querydomainvalid (if the query
+ * domain contains bad characters) or adns_s_answerdomaininvalid (if
+ * the answer contains bad characters).
+ * 
+ * In queries _with_ qf_quoteok_*, domains in the query or response
+ * may contain any characters, quoted according to RFC1035 5.1.  On
+ * input to adns, the char* is a pointer to the interior of a "
+ * delimited string, except that " may appear in it unquoted.  On
+ * output, the char* is a pointer to a string which would be legal
+ * either inside or outside " delimiters; any character which isn't
+ * legal in a hostname (ie alphanumeric or hyphen) or one of _ / +
+ * (the three other punctuation characters commonly abused in domain
+ * names) will be quoted, as \X if it is a printing ASCII character or
+ * \DDD otherwise.
+ *
+ * If the query goes via a CNAME then the canonical name (ie, the
+ * thing that the CNAME record refers to) is usually allowed to
+ * contain any characters, which will be quoted as above.  With
+ * adns_qf_quotefail_cname you get adns_s_answerdomaininvalid when
+ * this happens.  (This is a change from version 0.4 and earlier, in
+ * which failing the query was the default, and you had to say
+ * adns_qf_quoteok_cname to avoid this; that flag is now deprecated.)
+ *
+ * In version 0.4 and earlier, asking for _raw records containing
+ * mailboxes without specifying _qf_quoteok_anshost was silly.  This
+ * is no longer the case.  In this version only parts of responses
+ * that are actually supposed to be hostnames will be refused by
+ * default if quote-requiring characters are found.
+ */
+
+/*
+ * If you ask for an RR which contains domains which are actually
+ * encoded mailboxes, and don't ask for the _raw version, then adns
+ * returns the mailbox formatted suitably for an RFC822 recipient
+ * header field.  The particular format used is that if the mailbox
+ * requires quoting according to the rules in RFC822 then the
+ * local-part is quoted in double quotes, which end at the next
+ * unescaped double quote (\ is the escape char, and is doubled, and
+ * is used to escape only \ and ").  If the local-part is legal
+ * without quoting according to RFC822, it is presented as-is.  In any
+ * case the local-part is followed by an @ and the domain.  The domain
+ * will not contain any characters not legal in hostnames.
+ *
+ * Unquoted local-parts may contain any printing 7-bit ASCII
+ * except the punctuation characters ( ) < > @ , ; : \ " [ ]
+ * I.e. they may contain alphanumerics, and the following
+ * punctuation characters:  ! # % ^ & * - _ = + { } .
+ *
+ * adns will reject local parts containing control characters (byte
+ * values 0-31, 127-159, and 255) - these appear to be legal according
+ * to RFC822 (at least 0-127) but are clearly a bad idea.  RFC1035
+ * syntax does not make any distinction between a single RFC822
+ * quoted-string containing full stops, and a series of quoted-strings
+ * separated by full stops; adns will return anything that isn't all
+ * valid atoms as a single quoted-string.  RFC822 does not allow
+ * high-bit-set characters at all, but adns does allow them in
+ * local-parts, treating them as needing quoting.
+ *
+ * If you ask for the domain with _raw then _no_ checking is done
+ * (even on the host part, regardless of adns_qf_quoteok_anshost), and
+ * you just get the domain name in master file format.
+ *
+ * If no mailbox is supplied the returned string will be `.' in either
+ * case.
+ */
+
+typedef enum {
+  adns_s_ok,
+
+  /* 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_allservfail,
+  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_rcodeformaterror,
+  adns_s_rcodenotimplemented,
+  adns_s_rcoderefused,
+  adns_s_rcodeunknown,
+
+  adns_s_max_tempfail= 99,
+
+  /* remote configuration errors */
+  adns_s_inconsistent, /* PTR gives domain whose A does not exist and match */
+  adns_s_prohibitedcname, /* CNAME found where eg A expected (not if _qf_loosecname) */
+  adns_s_answerdomaininvalid,
+  adns_s_answerdomaintoolong,
+  adns_s_invaliddata,
+  
+  adns_s_max_misconfig= 199,
+
+  /* permanent problems with the query */
+  adns_s_querydomainwrong,
+  adns_s_querydomaininvalid,
+  adns_s_querydomaintoolong,
+  
+  adns_s_max_misquery= 299,
+
+  /* permanent errors */
+  adns_s_nxdomain,
+  adns_s_nodata,
+
+  adns_s_max_permfail= 499
+  
+} adns_status;
+
+typedef struct {
+  int len;
+  union {
+    struct sockaddr sa;
+    struct sockaddr_in inet;
+  } addr;
+} adns_rr_addr;
+
+typedef struct {
+  char *host;
+  adns_status astatus;
+  int naddrs; /* temp fail => -1, perm fail => 0, s_ok => >0 */
+  adns_rr_addr *addrs;
+} adns_rr_hostaddr;
+
+typedef struct {
+  char *(array[2]);
+} adns_rr_strpair;
+
+typedef struct {
+  int i;
+  adns_rr_hostaddr ha;
+} adns_rr_inthostaddr;
+
+typedef struct {
+  /* Used both for mx_raw, in which case i is the preference and str the domain,
+   * and for txt, in which case each entry has i for the `text' length,
+   * and str for the data (which will have had an extra nul appended
+   * so that if it was plain text it is now a null-terminated string).
+   */
+  int i;
+  char *str;
+} adns_rr_intstr;
+
+typedef struct {
+  adns_rr_intstr array[2];
+} adns_rr_intstrpair;
+
+typedef struct {
+  char *mname, *rname;
+  unsigned long serial, refresh, retry, expire, minimum;
+} adns_rr_soa;
+
+typedef struct {
+  adns_status status;
+  char *cname; /* always NULL if query was for CNAME records */
+  char *owner; /* only set if requested in query flags, and may be 0 on error anyway */
+  adns_rrtype type; /* guaranteed to be same as in query */
+  time_t expires; /* expiry time, defined only if _s_ok, nxdomain or nodata. NOT TTL! */
+  int nrrs, rrsz; /* nrrs is 0 if an error occurs */
+  union {
+    void *untyped;
+    unsigned char *bytes;
+    char *(*str);                     /* ns_raw, cname, ptr, ptr_raw */
+    adns_rr_intstr *(*manyistr);      /* txt (list of strings ends with i=-1, str=0) */
+    adns_rr_addr *addr;               /* addr */
+    struct in_addr *inaddr;           /* a */
+    adns_rr_hostaddr *hostaddr;       /* ns */
+    adns_rr_intstrpair *intstrpair;   /* hinfo */
+    adns_rr_strpair *strpair;         /* rp, rp_raw */
+    adns_rr_inthostaddr *inthostaddr; /* mx */
+    adns_rr_intstr *intstr;           /* mx_raw */
+    adns_rr_soa *soa;                 /* soa, soa_raw */
+  } rrs;
+} adns_answer;
+
+/* Memory management:
+ *  adns_state and adns_query are actually pointers to malloc'd state;
+ *  On submission questions are copied, including the owner domain;
+ *  Answers are malloc'd as a single piece of memory; pointers in the
+ *  answer struct point into further memory in the answer.
+ * query_io:
+ *  Must always be non-null pointer;
+ *  If *query_io is 0 to start with then any query may be returned;
+ *  If *query_io is !0 adns_query then only that query may be returned.
+ *  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.
+ *
+ *  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) EAGAIN is
+ *  returned: if no (appropriate) requests are done adns_check returns
+ *  EAGAIN; 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,
+             FILE *diagfile /*0=>stderr*/);
+
+int adns_init_strcfg(adns_state *newstate_r, adns_initflags flags,
+                    FILE *diagfile /*0=>discard*/, const char *configtext);
+
+/* Configuration:
+ *  adns_init reads /etc/resolv.conf, which is expected to be (broadly
+ *  speaking) in the format expected by libresolv, and then
+ *  /etc/resolv-adns.conf if it exists.  adns_init_strcfg is instead
+ *  passed a string which is interpreted as if it were the contents of
+ *  resolv.conf or resolv-adns.conf.  In general, configuration which
+ *  is set later overrides any that is set earlier.
+ *
+ * Standard directives understood in resolv[-adns].conf:
+ * 
+ *  nameserver <address>
+ *   Must be followed by the IP address of a nameserver.  Several
+ *   nameservers may be specified, and they will be tried in the order
+ *   found.  There is a compiled in limit, currently 5, on the number
+ *   of nameservers.  (libresolv supports only 3 nameservers.)
+ *
+ *  search <domain> ...
+ *   Specifies the search list for queries which specify
+ *   adns_qf_search.  This is a list of domains to append to the query
+ *   domain.  The query domain will be tried as-is either before all
+ *   of these or after them, depending on the ndots option setting
+ *   (see below).
+ *
+ *  domain <domain>
+ *   This is present only for backward compatibility with obsolete
+ *   versions of libresolv.  It should not be used, and is interpreted
+ *   by adns as if it were `search' - note that this is subtly
+ *   different to libresolv's interpretation of this directive.
+ *
+ *  sortlist <addr>/<mask> ...
+ *   Should be followed by a sequence of IP-address and netmask pairs,
+ *   separated by spaces.  They may be specified as
+ *   eg. 172.30.206.0/24 or 172.30.206.0/255.255.255.0.  Currently up
+ *   to 15 pairs may be specified (but note that libresolv only
+ *   supports up to 10).
+ *
+ *  options
+ *   Should followed by one or more options, separated by spaces.
+ *   Each option consists of an option name, followed by optionally
+ *   a colon and a value.  Options are listed below.
+ *
+ * Non-standard directives understood in resolv[-adns].conf:
+ *
+ *  clearnameservers
+ *   Clears the list of nameservers, so that further nameserver lines
+ *   start again from the beginning.
+ *
+ *  include <filename>
+ *   The specified file will be read.
+ *
+ * Additionally, adns will ignore lines in resolv[-adns].conf which
+ * start with a #.
+ *
+ * Standard options understood:
+ *
+ *  debug
+ *   Enables debugging output from the resolver, which will be written
+ *   to stderr.
+ *
+ *  ndots:<count>
+ *   Affects whether queries with adns_qf_search will be tried first
+ *   without adding domains from the searchlist, or whether the bare
+ *   query domain will be tried last.  Queries which contain at least
+ *   <count> dots will be tried bare first.  The default is 1.
+ *
+ * Non-standard options understood:
+ *
+ *  adns_checkc:none
+ *  adns_checkc:entex
+ *  adns_checkc:freq
+ *   Changes the consistency checking frequency; this overrides the
+ *   setting of adns_if_check_entex, adns_if_check_freq, or neither,
+ *   in the flags passed to adns_init.
+ * 
+ * There are a number of environment variables which can modify the
+ * behaviour of adns.  They take effect only if adns_init is used, and
+ * the caller of adns_init can disable them using adns_if_noenv.  In
+ * each case there is both a FOO and an ADNS_FOO; the latter is
+ * interpreted later so that it can override the former.  Unless
+ * otherwise stated, environment variables are interpreted after
+ * resolv[-adns].conf are read, in the order they are listed here.
+ *
+ *  RES_CONF, ADNS_RES_CONF
+ *   A filename, whose contets are in the format of resolv.conf.
+ *
+ *  RES_CONF_TEXT, ADNS_RES_CONF_TEXT
+ *   A string in the format of resolv.conf.
+ *
+ *  RES_OPTIONS, ADNS_RES_OPTIONS
+ *   These are parsed as if they appeared in the `options' line of a
+ *   resolv.conf.  In addition to being parsed at this point in the
+ *   sequence, they are also parsed at the very beginning before
+ *   resolv.conf or any other environment variables are read, so that
+ *   any debug option can affect the processing of the configuration.
+ *
+ *  LOCALDOMAIN, ADNS_LOCALDOMAIN
+ *   These are interpreted as if their contents appeared in a `search'
+ *   line in resolv.conf.
+ */
+
+int adns_synchronous(adns_state ads,
+                    const char *owner,
+                    adns_rrtype type,
+                    adns_queryflags 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 some of the asynch-io event
+ * processing functions to actually get things to happen.
+ */
+
+int adns_submit(adns_state ads,
+               const char *owner,
+               adns_rrtype type,
+               adns_queryflags flags,
+               void *context,
+               adns_query *query_r);
+
+int adns_submit_callback(adns_state ads,
+               const char *owner,
+               adns_rrtype type,
+               adns_queryflags flags,
+               void *context,
+               adns_query *query_r,
+               void (*callback) (adns_state, adns_query, void *));
+
+/* The owner should be quoted in master file format. */
+
+int adns_check(adns_state ads,
+              adns_query *query_io,
+              adns_answer **answer_r,
+              void **context_r);
+
+int adns_wait(adns_state ads,
+             adns_query *query_io,
+             adns_answer **answer_r,
+             void **context_r);
+
+/* same as adns_wait but uses poll(2) internally */
+int adns_wait_poll(adns_state ads,
+                  adns_query *query_io,
+                  adns_answer **answer_r,
+                  void **context_r);
+
+void adns_cancel(adns_query query);
+
+/* 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.
+ *
+ * _submit and _synchronous return ENOSYS if they don't understand the
+ * query type.
+ */
+
+int adns_submit_reverse(adns_state ads,
+                       const struct sockaddr *addr,
+                       adns_rrtype type,
+                       adns_queryflags flags,
+                       void *context,
+                       adns_query *query_r);
+/* type must be _r_ptr or _r_ptr_raw.  _qf_search is ignored.
+ * addr->sa_family must be AF_INET or you get ENOSYS.
+ */
+
+int adns_submit_reverse_any(adns_state ads,
+                           const struct sockaddr *addr,
+                           const char *rzone,
+                           adns_rrtype type,
+                           adns_queryflags flags,
+                           void *context,
+                           adns_query *query_r);
+/* For RBL-style reverse `zone's; look up
+ *   <reversed-address>.<zone>
+ * Any type is allowed.  _qf_search is ignored.
+ * addr->sa_family must be AF_INET or you get ENOSYS.
+ */
+
+void adns_finish(adns_state ads);
+/* You may call this even if you have queries outstanding;
+ * they will be cancelled.
+ */
+
+
+void adns_forallqueries_begin(adns_state ads);
+adns_query adns_forallqueries_next(adns_state ads, void **context_r);
+/* Iterator functions, which you can use to loop over the outstanding
+ * (submitted but not yet successfuly checked/waited) queries.
+ *
+ * You can only have one iteration going at once.  You may call _begin
+ * at any time; after that, an iteration will be in progress.  You may
+ * only call _next when an iteration is in progress - anything else
+ * may coredump.  The iteration remains in progress until _next
+ * returns 0, indicating that all the queries have been walked over,
+ * or ANY other adns function is called with the same adns_state (or a
+ * query in the same adns_state).  There is no need to explicitly
+ * finish an iteration.
+ *
+ * context_r may be 0.  *context_r may not be set when _next returns 0.
+ */
+
+void adns_checkconsistency(adns_state ads, adns_query qu);
+/* Checks the consistency of adns's internal data structures.
+ * If any error is found, the program will abort().
+ * You may pass 0 for qu; if you pass non-null then additional checks
+ * are done to make sure that qu is a valid query.
+ */
+
+/*
+ * Example expected/legal calling sequence for submit/check/wait:
+ *  adns_init
+ *  adns_submit 1
+ *  adns_submit 2
+ *  adns_submit 3
+ *  adns_wait 1
+ *  adns_check 3 -> EAGAIN
+ *  adns_wait 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_
+ * use 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, and it will set the timeout to zero if a query
+ * finishes in _beforeselect.
+ */
+
+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_beforeselect
+ *   select
+ *   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.
+ *
+ * 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 either 0 (if it is not interested in
+ * any fds) or ERANGE (if it is).
+ *
+ * NOTE that (unless 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.
+ *
+ * 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, or
+ * alternatively, timeout_io may be 0.  (Alternatively, the caller may
+ * use _beforeselect with timeout_io==0 to find out about file
+ * descriptors, and use _firsttimeout is used to find out when adns
+ * might want to time something out.)
+ *
+ * adns_beforepoll will return 0 on success, and will not fail for any
+ * reason other than the fds buffer being too small (ERANGE).
+ *
+ * This call will never actually do any I/O.  If you supply the
+ * current time it will not change the fds that adns is using or the
+ * timeouts it wants.
+ *
+ * In any case this call 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 void *datap, char **data_r);
+/*
+ * Get information about a query type, or convert reply data to a
+ * textual form.  type must be specified, and the official name of the
+ * corresponding RR type will be returned in *rrtname_r, and
+ * information about the processing style in *fmtname_r.  The length
+ * of the table entry in an answer for that type will be returned in
+ * in *len_r.  Any or all of rrtname_r, fmtname_r and len_r may be 0.
+ * If fmtname_r is non-null then *fmtname_r may be null on return,
+ * indicating that no special processing is involved.
+ *
+ * data_r be must be non-null iff datap is.  In this case *data_r will
+ * be set to point to a string pointing to a representation of the RR
+ * data in master file format.  (The owner name, timeout, class and
+ * type will not be present - only the data part of the RR.)  The
+ * memory will have been obtained from malloc() and must be freed by
+ * the caller.
+ *
+ * Usually this routine will succeed.  Possible errors include:
+ *  adns_s_nomemory
+ *  adns_s_rrtypeunknown
+ *  adns_s_invaliddata (*datap contained garbage)
+ * If an error occurs then no memory has been allocated,
+ * and *rrtname_r, *fmtname_r, *len_r and *data_r are undefined.
+ *
+ * There are some adns-invented data formats which are not official
+ * master file formats.  These include:
+ *
+ * Mailboxes if __qtf_mail822: these are just included as-is.
+ *
+ * Addresses (adns_rr_addr): these may be of pretty much any type.
+ * The representation is in two parts: first, a word for the address
+ * family (ie, in AF_XXX, the XXX), and then one or more items for the
+ * address itself, depending on the format.  For an IPv4 address the
+ * syntax is INET followed by the dotted quad (from inet_ntoa).
+ * Currently only IPv4 is supported.
+ *
+ * Text strings (as in adns_rr_txt) appear inside double quotes, and
+ * use \" and \\ to represent " and \, and \xHH to represent
+ * characters not in the range 32-126.
+ *
+ * Hostname with addresses (adns_rr_hostaddr): this consists of the
+ * hostname, as usual, followed by the adns_status value, as an
+ * abbreviation, and then a descriptive string (encoded as if it were
+ * a piece of text), for the address lookup, followed by zero or more
+ * addresses enclosed in ( and ).  If the result was a temporary
+ * failure, then a single ?  appears instead of the ( ).  If the
+ * result was a permanent failure then an empty pair of parentheses
+ * appears (which a space in between).  For example, one of the NS
+ * records for greenend.org.uk comes out like
+ *  ns.chiark.greenend.org.uk ok "OK" ( INET 195.224.76.132 )
+ * an MX referring to a nonexistent host might come out like:
+ *  50 sun2.nsfnet-relay.ac.uk nxdomain "No such domain" ( )
+ * and if nameserver information is not available you might get:
+ *  dns2.spong.dyn.ml.org timeout "DNS query timed out" ?
+ */
+
+const char *adns_strerror(adns_status st);
+const char *adns_errabbrev(adns_status st);
+const char *adns_errtypeabbrev(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".  adns_errtypeabbrev returns the abbreviation of the
+ * error class: ie, for values up to adns_s_max_XXX it will return the
+ * string XXX.  You MUST NOT call these functions with status values
+ * not returned by the same adns library.
+ */
+
+int adns_get_fd(adns_state ads);
+
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+#endif
diff --git a/adns/src/adns.make b/adns/src/adns.make
new file mode 100644 (file)
index 0000000..14b70bd
--- /dev/null
@@ -0,0 +1,25 @@
+# src/adns.make - library definitions, including list of object files
+# 
+#  This file is
+#    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+#
+#  It is part of adns, which is
+#    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+#    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+#  
+#  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. 
+
+LIBOBJS=       types.o event.o query.o reply.o general.o setup.o transmit.o \
+               parse.o poll.o check.o
diff --git a/adns/src/check.c b/adns/src/check.c
new file mode 100644 (file)
index 0000000..7cca534
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * check.c
+ * - consistency checks
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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 "internal.h"
+
+void adns_checkconsistency(adns_state ads, adns_query qu) {
+  adns__consistency(ads,qu,cc_user);
+}
+
+#define DLIST_CHECK(list, nodevar, part, body)                                 \
+  if ((list).head) {                                                           \
+    assert(! (list).head->part back);                                          \
+    for ((nodevar)= (list).head; (nodevar); (nodevar)= (nodevar)->part next) { \
+      assert((nodevar)->part next                                              \
+            ? (nodevar) == (nodevar)->part next->part back                     \
+            : (nodevar) == (list).tail);                                       \
+      body                                                                     \
+    }                                                                          \
+  }
+
+#define DLIST_ASSERTON(node, nodevar, list, part)                              \
+  do {                                                                         \
+    for ((nodevar)= (list).head;                                               \
+        (nodevar) != (node);                                                   \
+        (nodevar)= (nodevar)->part next) {                                     \
+      assert((nodevar));                                                       \
+    }                                                                          \
+  } while(0)
+
+static void checkc_query_alloc(adns_state ads, adns_query qu) {
+  allocnode *an;
+
+  DLIST_CHECK(qu->allocations, an, , {
+  });
+}
+
+static void checkc_query(adns_state ads, adns_query qu) {
+  adns_query child;
+
+  assert(qu->udpnextserver < ads->nservers);
+  assert(!(qu->udpsent & (~0UL << ads->nservers)));
+  assert(qu->search_pos <= ads->nsearchlist);
+  if (qu->parent) DLIST_ASSERTON(qu, child, qu->parent->children, siblings.);
+}
+
+static void checkc_notcpbuf(adns_state ads) {
+  assert(!ads->tcpsend.used);
+  assert(!ads->tcprecv.used);
+  assert(!ads->tcprecv_skip);
+}
+
+static void checkc_global(adns_state ads) {
+  int i;
+  
+  assert(ads->udpsocket >= 0);
+
+  for (i=0; i<ads->nsortlist; i++)
+    assert(!(ads->sortlist[i].base.s_addr & ~ads->sortlist[i].mask.s_addr));
+
+  assert(ads->tcpserver >= 0 && ads->tcpserver < ads->nservers);
+  
+  switch (ads->tcpstate) {
+  case server_connecting:
+    assert(ads->tcpsocket >= 0);
+    checkc_notcpbuf(ads);
+    break;
+  case server_disconnected:
+  case server_broken:
+    assert(ads->tcpsocket == -1);
+    checkc_notcpbuf(ads);
+    break;
+  case server_ok:
+    assert(ads->tcpsocket >= 0);
+    assert(ads->tcprecv_skip <= ads->tcprecv.used);
+    break;
+  default:
+    assert(!"ads->tcpstate value");
+  }
+
+  assert(ads->searchlist || !ads->nsearchlist);
+}
+
+static void checkc_queue_udpw(adns_state ads) {
+  adns_query qu;
+  
+  DLIST_CHECK(ads->udpw, qu, , {
+    assert(qu->state==query_tosend);
+    assert(qu->retries <= UDPMAXRETRIES);
+    assert(qu->udpsent);
+    assert(!qu->children.head && !qu->children.tail);
+    checkc_query(ads,qu);
+    checkc_query_alloc(ads,qu);
+  });
+}
+
+static void checkc_queue_tcpw(adns_state ads) {
+  adns_query qu;
+  
+  DLIST_CHECK(ads->tcpw, qu, , {
+    assert(qu->state==query_tcpw);
+    assert(!qu->children.head && !qu->children.tail);
+    assert(qu->retries <= ads->nservers+1);
+    checkc_query(ads,qu);
+    checkc_query_alloc(ads,qu);
+  });
+}
+
+static void checkc_queue_childw(adns_state ads) {
+  adns_query parent, child;
+
+  DLIST_CHECK(ads->childw, parent, , {
+    assert(parent->state == query_childw);
+    assert(parent->children.head);
+    DLIST_CHECK(parent->children, child, siblings., {
+      assert(child->parent == parent);
+      assert(child->state != query_done);
+    });
+    checkc_query(ads,parent);
+    checkc_query_alloc(ads,parent);
+  });
+}
+
+static void checkc_queue_output(adns_state ads) {
+  adns_query qu;
+  
+  DLIST_CHECK(ads->output, qu, , {
+    assert(qu->state == query_done);
+    assert(!qu->children.head && !qu->children.tail);
+    assert(!qu->parent);
+    assert(!qu->allocations.head && !qu->allocations.tail);
+    checkc_query(ads,qu);
+  });
+}
+
+void adns__consistency(adns_state ads, adns_query qu, consistency_checks cc) {
+  adns_query search;
+  
+  switch (cc) {
+  case cc_user:
+    break;
+  case cc_entex:
+    if (!(ads->iflags & adns_if_checkc_entex)) return;
+    break;
+  case cc_freq:
+    if ((ads->iflags & adns_if_checkc_freq) != adns_if_checkc_freq) return;
+    break;
+  default:
+    abort();
+  }
+
+  checkc_global(ads);
+  checkc_queue_udpw(ads);
+  checkc_queue_tcpw(ads);
+  checkc_queue_childw(ads);
+  checkc_queue_output(ads);
+
+  if (qu) {
+    switch (qu->state) {
+    case query_tosend:
+      DLIST_ASSERTON(qu, search, ads->udpw, );
+      break;
+    case query_tcpw:
+      DLIST_ASSERTON(qu, search, ads->tcpw, );
+      break;
+    case query_childw:
+      DLIST_ASSERTON(qu, search, ads->childw, );
+      break;
+    case query_done:
+      DLIST_ASSERTON(qu, search, ads->output, );
+      break;
+    default:
+      assert(!"specific query state");
+    }
+  }
+}
diff --git a/adns/src/config.h b/adns/src/config.h
new file mode 100644 (file)
index 0000000..255fadf
--- /dev/null
@@ -0,0 +1,106 @@
+/* src/config.h.  Generated automatically by configure.  */
+/* src/config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if inline functions a la GCC are available.  */
+#define HAVE_INLINE 1
+
+/* Define if function attributes a la GCC 2.5 and higher are available.  */
+#define HAVE_GNUC25_ATTRIB 1
+
+/* Define if constant functions a la GCC 2.5 and higher are available.  */
+#define HAVE_GNUC25_CONST 1
+
+/* Define if nonreturning functions a la GCC 2.5 and higher are available.  */
+#define HAVE_GNUC25_NORETURN 1
+
+/* Define if printf-format argument lists a la GCC are available.  */
+#define HAVE_GNUC25_PRINTFFORMAT 1
+
+/* Define if we want to include rpc/types.h.  Crap BSDs put INADDR_LOOPBACK there. */
+/* #undef HAVEUSE_RPCTYPES_H */
+
+/* Define if you have the poll function.  */
+#define HAVE_POLL 1
+
+/* Define if you have the <sys/select.h> header file.  */
+#define HAVE_SYS_SELECT_H 1
+
+/* 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: */
+
+#ifndef HAVE_INLINE
+#define inline
+#endif
+
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#else
+/* kludge it up */
+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
+#define FUNCATTR(x) __attribute__(x)
+#else
+#define FUNCATTR(x)
+#endif
+#endif
+
+/* GNU C printf formats, or null. */
+#ifndef ATTRPRINTF
+#ifdef HAVE_GNUC25_PRINTFFORMAT
+#define ATTRPRINTF(si,tc) format(printf,si,tc)
+#else
+#define ATTRPRINTF(si,tc)
+#endif
+#endif
+#ifndef PRINTFFORMAT
+#define PRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc)))
+#endif
+
+/* GNU C nonreturning functions, or null. */
+#ifndef ATTRNORETURN
+#ifdef HAVE_GNUC25_NORETURN
+#define ATTRNORETURN noreturn
+#else
+#define ATTRNORETURN
+#endif
+#endif
+#ifndef NONRETURNING
+#define NONRETURNING FUNCATTR((ATTRNORETURN))
+#endif
+
+/* Combination of both the above. */
+#ifndef NONRETURNPRINTFFORMAT
+#define NONRETURNPRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc),ATTRNORETURN))
+#endif
+
+/* GNU C constant functions, or null. */
+#ifndef ATTRCONST
+#ifdef HAVE_GNUC25_CONST
+#define ATTRCONST const
+#else
+#define ATTRCONST
+#endif
+#endif
+#ifndef CONSTANT
+#define CONSTANT FUNCATTR((ATTRCONST))
+#endif
+
+#ifdef HAVEUSE_RPCTYPES_H
+#include <rpc/types.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
diff --git a/adns/src/config.h.in b/adns/src/config.h.in
new file mode 100644 (file)
index 0000000..a22abe8
--- /dev/null
@@ -0,0 +1,105 @@
+/* src/config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define if inline functions a la GCC are available.  */
+#undef HAVE_INLINE
+
+/* Define if function attributes a la GCC 2.5 and higher are available.  */
+#undef HAVE_GNUC25_ATTRIB
+
+/* Define if constant functions a la GCC 2.5 and higher are available.  */
+#undef HAVE_GNUC25_CONST
+
+/* Define if nonreturning functions a la GCC 2.5 and higher are available.  */
+#undef HAVE_GNUC25_NORETURN
+
+/* Define if printf-format argument lists a la GCC are available.  */
+#undef HAVE_GNUC25_PRINTFFORMAT
+
+/* Define if we want to include rpc/types.h.  Crap BSDs put INADDR_LOOPBACK there. */
+#undef HAVEUSE_RPCTYPES_H
+
+/* Define if you have the poll function.  */
+#undef HAVE_POLL
+
+/* Define if you have the <sys/select.h> header file.  */
+#undef HAVE_SYS_SELECT_H
+
+/* 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: */
+
+#ifndef HAVE_INLINE
+#define inline
+#endif
+
+#ifdef HAVE_POLL
+#include <sys/poll.h>
+#else
+/* kludge it up */
+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
+#define FUNCATTR(x) __attribute__(x)
+#else
+#define FUNCATTR(x)
+#endif
+#endif
+
+/* GNU C printf formats, or null. */
+#ifndef ATTRPRINTF
+#ifdef HAVE_GNUC25_PRINTFFORMAT
+#define ATTRPRINTF(si,tc) format(printf,si,tc)
+#else
+#define ATTRPRINTF(si,tc)
+#endif
+#endif
+#ifndef PRINTFFORMAT
+#define PRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc)))
+#endif
+
+/* GNU C nonreturning functions, or null. */
+#ifndef ATTRNORETURN
+#ifdef HAVE_GNUC25_NORETURN
+#define ATTRNORETURN noreturn
+#else
+#define ATTRNORETURN
+#endif
+#endif
+#ifndef NONRETURNING
+#define NONRETURNING FUNCATTR((ATTRNORETURN))
+#endif
+
+/* Combination of both the above. */
+#ifndef NONRETURNPRINTFFORMAT
+#define NONRETURNPRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc),ATTRNORETURN))
+#endif
+
+/* GNU C constant functions, or null. */
+#ifndef ATTRCONST
+#ifdef HAVE_GNUC25_CONST
+#define ATTRCONST const
+#else
+#define ATTRCONST
+#endif
+#endif
+#ifndef CONSTANT
+#define CONSTANT FUNCATTR((ATTRCONST))
+#endif
+
+#ifdef HAVEUSE_RPCTYPES_H
+#include <rpc/types.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
diff --git a/adns/src/dlist.h b/adns/src/dlist.h
new file mode 100644 (file)
index 0000000..44bf556
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * dlist.h
+ * - macros for handling doubly linked lists
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999 Tony Finch <dot@dotat.at>
+ *  
+ *  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. 
+ */
+
+#ifndef ADNS_DLIST_H_INCLUDED
+#define ADNS_DLIST_H_INCLUDED
+
+#define LIST_INIT(list) ((list).head= (list).tail= 0)
+#define LINK_INIT(link) ((link).next= (link).back= 0)
+
+#define LIST_UNLINK_PART(list,node,part) \
+  do { \
+    if ((node)->part back) (node)->part back->part next= (node)->part next; \
+      else                                  (list).head= (node)->part next; \
+    if ((node)->part next) (node)->part next->part back= (node)->part back; \
+      else                                  (list).tail= (node)->part back; \
+  } while(0)
+
+#define LIST_LINK_TAIL_PART(list,node,part) \
+  do { \
+    (node)->part next= 0; \
+    (node)->part back= (list).tail; \
+    if ((list).tail) (list).tail->part next= (node); else (list).head= (node); \
+    (list).tail= (node); \
+  } while(0)
+
+#define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,)
+#define LIST_LINK_TAIL(list,node) LIST_LINK_TAIL_PART(list,node,)
+
+#endif
diff --git a/adns/src/event.c b/adns/src/event.c
new file mode 100644 (file)
index 0000000..53790d4
--- /dev/null
@@ -0,0 +1,734 @@
+/*
+ * event.c
+ * - event loop core
+ * - TCP connection management
+ * - user-visible check/wait and event-loop-related functions
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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 <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "internal.h"
+#include "tvarith.h"
+
+/* TCP connection management. */
+
+static void tcp_close(adns_state ads) {
+  int serv;
+  
+  serv= ads->tcpserver;
+  close(ads->tcpsocket);
+  ads->tcpsocket= -1;
+  ads->tcprecv.used= ads->tcprecv_skip= ads->tcpsend.used= 0;
+}
+
+void adns__tcp_broken(adns_state ads, const char *what, const char *why) {
+  int serv;
+  adns_query qu;
+  
+  assert(ads->tcpstate == server_connecting || ads->tcpstate == server_ok);
+  serv= ads->tcpserver;
+  if (what) adns__warn(ads,serv,0,"TCP connection failed: %s: %s",what,why);
+
+  if (ads->tcpstate == server_connecting) {
+    /* Counts as a retry for all the queries waiting for TCP. */
+    for (qu= ads->tcpw.head; qu; qu= qu->next)
+      qu->retries++;
+  }
+
+  tcp_close(ads);
+  ads->tcpstate= server_broken;
+  ads->tcpserver= (serv+1)%ads->nservers;
+}
+
+static void tcp_connected(adns_state ads, struct timeval now) {
+  adns_query qu, nqu;
+  
+  adns__debug(ads,ads->tcpserver,0,"TCP connected");
+  ads->tcpstate= server_ok;
+  for (qu= ads->tcpw.head; qu && ads->tcpstate == server_ok; qu= nqu) {
+    nqu= qu->next;
+    assert(qu->state == query_tcpw);
+    adns__querysend_tcp(qu,now);
+  }
+}
+
+void adns__tcp_tryconnect(adns_state ads, struct timeval now) {
+  int r, fd, tries;
+  struct sockaddr_in addr;
+  struct protoent *proto;
+
+  for (tries=0; tries<ads->nservers; tries++) {
+    switch (ads->tcpstate) {
+    case server_connecting:
+    case server_ok:
+    case server_broken:
+      return;
+    case server_disconnected:
+      break;
+    default:
+      abort();
+    }
+    
+    assert(!ads->tcpsend.used);
+    assert(!ads->tcprecv.used);
+    assert(!ads->tcprecv_skip);
+
+    proto= getprotobyname("tcp");
+    if (!proto) { adns__diag(ads,-1,0,"unable to find protocol no. for TCP !"); return; }
+#if 0
+    fd= socket(AF_INET,SOCK_STREAM,proto->p_proto);
+#else
+    fd = -1;
+    errno = EPERM;
+#endif
+    if (fd<0) {
+      adns__diag(ads,-1,0,"cannot create TCP socket: %s",strerror(errno));
+      return;
+    }
+    r= adns__setnonblock(ads,fd);
+    if (r) {
+      adns__diag(ads,-1,0,"cannot make TCP socket nonblocking: %s",strerror(r));
+      close(fd);
+      return;
+    }
+    memset(&addr,0,sizeof(addr));
+    addr.sin_family= AF_INET;
+    addr.sin_port= htons(DNS_PORT);
+    addr.sin_addr= ads->servers[ads->tcpserver].addr;
+    r= connect(fd,(const struct sockaddr*)&addr,sizeof(addr));
+    ads->tcpsocket= fd;
+    ads->tcpstate= server_connecting;
+    if (r==0) { tcp_connected(ads,now); return; }
+    if (errno == EWOULDBLOCK || errno == EINPROGRESS) {
+      ads->tcptimeout= now;
+      timevaladd(&ads->tcptimeout,TCPCONNMS);
+      return;
+    }
+    adns__tcp_broken(ads,"connect",strerror(errno));
+    ads->tcpstate= server_disconnected;
+  }
+}
+
+/* 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_immed(struct timeval **tv_io, struct timeval *tvbuf) {
+  struct timeval *rbuf;
+
+  if (!tv_io) return;
+
+  rbuf= *tv_io;
+  if (!rbuf) { *tv_io= rbuf= tvbuf; }
+
+  timerclear(rbuf);
+}
+    
+static void inter_maxto(struct timeval **tv_io, struct timeval *tvbuf,
+                       struct timeval maxto) {
+  struct timeval *rbuf;
+
+  if (!tv_io) return;
+  rbuf= *tv_io;
+  if (!rbuf) {
+    *tvbuf= maxto; *tv_io= tvbuf;
+  } else {
+    if (timercmp(rbuf,&maxto,>)) *rbuf= maxto;
+  }
+/*fprintf(stderr,"inter_maxto maxto=%ld.%06ld result=%ld.%06ld\n",
+       maxto.tv_sec,maxto.tv_usec,(**tv_io).tv_sec,(**tv_io).tv_usec);*/
+}
+
+static void inter_maxtoabs(struct timeval **tv_io, struct timeval *tvbuf,
+                          struct timeval now, struct timeval maxtime) {
+  /* tv_io may be 0 */
+  ldiv_t dr;
+
+/*fprintf(stderr,"inter_maxtoabs now=%ld.%06ld maxtime=%ld.%06ld\n",
+       now.tv_sec,now.tv_usec,maxtime.tv_sec,maxtime.tv_usec);*/
+  if (!tv_io) return;
+  maxtime.tv_sec -= (now.tv_sec+2);
+  maxtime.tv_usec -= (now.tv_usec-2000000);
+  dr= ldiv(maxtime.tv_usec,1000000);
+  maxtime.tv_sec += dr.quot;
+  maxtime.tv_usec -= dr.quot*1000000;
+  if (maxtime.tv_sec<0) timerclear(&maxtime);
+  inter_maxto(tv_io,tvbuf,maxtime);
+}
+
+static void timeouts_queue(adns_state ads, int act,
+                          struct timeval **tv_io, struct timeval *tvbuf,
+                          struct timeval now, struct query_queue *queue) {
+  adns_query qu, nqu;
+  
+  for (qu= queue->head; qu; qu= nqu) {
+    nqu= qu->next;
+    if (!timercmp(&now,&qu->timeout,>)) {
+      inter_maxtoabs(tv_io,tvbuf,now,qu->timeout);
+    } else {
+      if (!act) { inter_immed(tv_io,tvbuf); return; }
+      LIST_UNLINK(*queue,qu);
+      if (qu->state != query_tosend) {
+       adns__query_fail(qu,adns_s_timeout);
+      } else {
+       adns__query_send(qu,now);
+      }
+      nqu= queue->head;
+    }
+  }
+}
+
+static void tcp_events(adns_state ads, int act,
+                      struct timeval **tv_io, struct timeval *tvbuf,
+                      struct timeval now) {
+  adns_query qu, nqu;
+  
+  for (;;) {
+    switch (ads->tcpstate) {
+    case server_broken:
+      if (!act) { inter_immed(tv_io,tvbuf); return; }
+      for (qu= ads->tcpw.head; qu; qu= nqu) {
+       nqu= qu->next;
+       assert(qu->state == query_tcpw);
+       if (qu->retries > ads->nservers) {
+         LIST_UNLINK(ads->tcpw,qu);
+         adns__query_fail(qu,adns_s_allservfail);
+       }
+      }
+      ads->tcpstate= server_disconnected;
+    case server_disconnected: /* fall through */
+      if (!ads->tcpw.head) return;
+      if (!act) { inter_immed(tv_io,tvbuf); return; }
+      adns__tcp_tryconnect(ads,now);
+      break;
+    case server_ok:
+      if (ads->tcpw.head) return;
+      if (!ads->tcptimeout.tv_sec) {
+       assert(!ads->tcptimeout.tv_usec);
+       ads->tcptimeout= now;
+       timevaladd(&ads->tcptimeout,TCPIDLEMS);
+      }
+    case server_connecting: /* fall through */
+      if (!act || !timercmp(&now,&ads->tcptimeout,>)) {
+       inter_maxtoabs(tv_io,tvbuf,now,ads->tcptimeout);
+       return;
+      } {
+       /* TCP timeout has happened */
+       switch (ads->tcpstate) {
+       case server_connecting: /* failed to connect */
+         adns__tcp_broken(ads,"unable to make connection","timed out");
+         break;
+       case server_ok: /* idle timeout */
+         tcp_close(ads);
+         ads->tcpstate= server_disconnected;
+         return;
+       default:
+         abort();
+       }
+      }
+      break;
+    default:
+      abort();
+    }
+  }
+  return;
+}
+
+void adns__timeouts(adns_state ads, int act,
+                   struct timeval **tv_io, struct timeval *tvbuf,
+                   struct timeval now) {
+  timeouts_queue(ads,act,tv_io,tvbuf,now, &ads->udpw);
+  timeouts_queue(ads,act,tv_io,tvbuf,now, &ads->tcpw);
+  tcp_events(ads,act,tv_io,tvbuf,now);
+}
+
+void adns_firsttimeout(adns_state ads,
+                      struct timeval **tv_io, struct timeval *tvbuf,
+                      struct timeval now) {
+  adns__consistency(ads,0,cc_entex);
+  adns__timeouts(ads, 0, tv_io,tvbuf, now);
+  adns__consistency(ads,0,cc_entex);
+}
+
+void adns_processtimeouts(adns_state ads, const struct timeval *now) {
+  struct timeval tv_buf;
+
+  adns__consistency(ads,0,cc_entex);
+  adns__must_gettimeofday(ads,&now,&tv_buf);
+  if (now) adns__timeouts(ads, 1, 0,0, *now);
+  adns__consistency(ads,0,cc_entex);
+}
+
+/* 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:
+  case server_broken:
+    return 1;
+  case server_connecting:
+    pollfds_buf[1].events= POLLOUT;
+    break;
+  case server_ok:
+    pollfds_buf[1].events= ads->tcpsend.used ? POLLIN|POLLOUT|POLLPRI : POLLIN|POLLPRI;
+    break;
+  default:
+    abort();
+  }
+  pollfds_buf[1].fd= ads->tcpsocket;
+  return 2;
+}
+
+int adns_get_fd(adns_state ads)
+{
+  return ads->udpsocket;
+}
+
+int adns_processreadable(adns_state ads, int fd, const struct timeval *now) {
+  int want, dgramlen, r, udpaddrlen, serv, old_skip;
+  byte udpbuf[DNS_MAXUDP];
+  struct sockaddr_in udpaddr;
+  
+  adns__consistency(ads,0,cc_entex);
+
+  switch (ads->tcpstate) {
+  case server_disconnected:
+  case server_broken:
+  case server_connecting:
+    break;
+  case server_ok:
+    if (fd != ads->tcpsocket) break;
+    assert(!ads->tcprecv_skip);
+    do {
+      if (ads->tcprecv.used >= ads->tcprecv_skip+2) {
+       dgramlen= ((ads->tcprecv.buf[ads->tcprecv_skip]<<8) |
+                  ads->tcprecv.buf[ads->tcprecv_skip+1]);
+       if (ads->tcprecv.used >= ads->tcprecv_skip+2+dgramlen) {
+         old_skip= ads->tcprecv_skip;
+         ads->tcprecv_skip += 2+dgramlen;
+         adns__procdgram(ads, ads->tcprecv.buf+old_skip+2,
+                         dgramlen, ads->tcpserver, 1,*now);
+         continue;
+       } else {
+         want= 2+dgramlen;
+       }
+      } else {
+       want= 2;
+      }
+      ads->tcprecv.used -= ads->tcprecv_skip;
+      memmove(ads->tcprecv.buf,ads->tcprecv.buf+ads->tcprecv_skip,ads->tcprecv.used);
+      ads->tcprecv_skip= 0;
+      if (!adns__vbuf_ensure(&ads->tcprecv,want)) { r= ENOMEM; goto xit; }
+      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) { r= 0; goto xit; }
+         if (errno==EINTR) continue;
+         if (errno_resources(errno)) { r= errno; goto xit; }
+       }
+       adns__tcp_broken(ads,"read",r?strerror(errno):"closed");
+      }
+    } while (ads->tcpstate == server_ok);
+    r= 0; goto xit;
+  default:
+    abort();
+  }
+  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) { r= 0; goto xit; }
+       if (errno == EINTR) continue;
+       if (errno_resources(errno)) { r= errno; goto xit; }
+       adns__warn(ads,-1,0,"datagram receive error: %s",strerror(errno));
+       r= 0; goto xit;
+      }
+      if (udpaddrlen != sizeof(udpaddr)) {
+       adns__diag(ads,-1,0,"datagram received with wrong address length %d"
+                  " (expected %lu)", udpaddrlen,
+                  (unsigned long)sizeof(udpaddr));
+       continue;
+      }
+      if (udpaddr.sin_family != AF_INET) {
+       adns__diag(ads,-1,0,"datagram received with wrong protocol family"
+                  " %u (expected %u)",udpaddr.sin_family,AF_INET);
+       continue;
+      }
+      if (ntohs(udpaddr.sin_port) != DNS_PORT) {
+       adns__diag(ads,-1,0,"datagram received from wrong port %u (expected %u)",
+                  ntohs(udpaddr.sin_port),DNS_PORT);
+       continue;
+      }
+      for (serv= 0;
+          serv < ads->nservers &&
+            ads->servers[serv].addr.s_addr != udpaddr.sin_addr.s_addr;
+          serv++);
+      if (serv >= ads->nservers) {
+       adns__warn(ads,-1,0,"datagram received from unknown nameserver %s",
+                  inet_ntoa(udpaddr.sin_addr));
+       continue;
+      }
+      adns__procdgram(ads,udpbuf,r,serv,0,*now);
+    }
+  }
+  r= 0;
+xit:
+  adns__consistency(ads,0,cc_entex);
+  return r;
+}
+
+int adns_processwriteable(adns_state ads, int fd, const struct timeval *now) {
+  int r;
+  
+  adns__consistency(ads,0,cc_entex);
+
+  switch (ads->tcpstate) {
+  case server_disconnected:
+  case server_broken:
+    break;
+  case server_connecting:
+    if (fd != ads->tcpsocket) break;
+    assert(ads->tcprecv.used==0);
+    assert(ads->tcprecv_skip==0);
+    for (;;) {
+      if (!adns__vbuf_ensure(&ads->tcprecv,1)) { r= ENOMEM; goto xit; }
+      r= read(ads->tcpsocket,&ads->tcprecv.buf,1);
+      if (r==0 || (r<0 && (errno==EAGAIN || errno==EWOULDBLOCK))) {
+       tcp_connected(ads,*now);
+       r= 0; goto xit;
+      }
+      if (r>0) {
+       adns__tcp_broken(ads,"connect/read","sent data before first request");
+       r= 0; goto xit;
+      }
+      if (errno==EINTR) continue;
+      if (errno_resources(errno)) { r= errno; goto xit; }
+      adns__tcp_broken(ads,"connect/read",strerror(errno));
+      r= 0; goto xit;
+    } /* not reached */
+  case server_ok:
+    if (fd != ads->tcpsocket) break;
+    while (ads->tcpsend.used) {
+      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) { r= 0; goto xit; }
+       if (errno_resources(errno)) { r= errno; goto xit; }
+       adns__tcp_broken(ads,"write",strerror(errno));
+       r= 0; goto xit;
+      } else if (r>0) {
+       ads->tcpsend.used -= r;
+       memmove(ads->tcpsend.buf,ads->tcpsend.buf+r,ads->tcpsend.used);
+      }
+    }
+    r= 0;
+    goto xit;
+  default:
+    abort();
+  }
+  r= 0;
+xit:
+  adns__consistency(ads,0,cc_entex);
+  return r;
+}
+  
+int adns_processexceptional(adns_state ads, int fd, const struct timeval *now) {
+  adns__consistency(ads,0,cc_entex);
+  switch (ads->tcpstate) {
+  case server_disconnected:
+  case server_broken:
+    break;
+  case server_connecting:
+  case server_ok:
+    if (fd != ads->tcpsocket) break;
+    adns__tcp_broken(ads,"poll/select","exceptional condition detected");
+    break;
+  default:
+    abort();
+  }
+  adns__consistency(ads,0,cc_entex);
+  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);
+  }
+}
+
+/* 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;
+  
+  adns__consistency(ads,0,cc_entex);
+
+  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) { inter_immed(tv_mod,tv_tobuf); goto xit; }
+    adns__timeouts(ads, 0, 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;
+
+xit:
+  adns__consistency(ads,0,cc_entex);
+}
+
+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 timeval tv_buf;
+  struct pollfd pollfds[MAX_POLLFDS];
+  int npollfds, i;
+
+  adns__consistency(ads,0,cc_entex);
+  adns__must_gettimeofday(ads,&now,&tv_buf);
+  if (!now) goto xit;
+  adns_processtimeouts(ads,now);
+
+  npollfds= adns__pollfds(ads,pollfds);
+  for (i=0; i<npollfds; i++) pollfds[i].revents= POLLIN|POLLOUT|POLLPRI;
+  adns__fdevents(ads,
+                pollfds,npollfds,
+                maxfd,readfds,writefds,exceptfds,
+                *now, 0);
+xit:
+  adns__consistency(ads,0,cc_entex);
+}
+
+/* General helpful functions. */
+
+void adns_globalsystemfailure(adns_state ads) {
+  adns__consistency(ads,0,cc_entex);
+
+  while (ads->udpw.head) adns__query_fail(ads->udpw.head, adns_s_systemfail);
+  while (ads->tcpw.head) adns__query_fail(ads->tcpw.head, adns_s_systemfail);
+  
+  switch (ads->tcpstate) {
+  case server_connecting:
+  case server_ok:
+    adns__tcp_broken(ads,0,0);
+    break;
+  case server_disconnected:
+  case server_broken:
+    break;
+  default:
+    abort();
+  }
+  adns__consistency(ads,0,cc_entex);
+}
+
+int adns_processany(adns_state ads) {
+  int r, i;
+  struct timeval now;
+  struct pollfd pollfds[MAX_POLLFDS];
+  int npollfds;
+
+  adns__consistency(ads,0,cc_entex);
+
+  r= gettimeofday(&now,0);
+  if (!r) adns_processtimeouts(ads,&now);
+
+  /* We just use adns__fdevents to loop over the fd's trying them.
+   * This seems more sensible than calling select, since we're most
+   * likely just to want to do a read on one or two fds anyway.
+   */
+  npollfds= adns__pollfds(ads,pollfds);
+  for (i=0; i<npollfds; i++) pollfds[i].revents= pollfds[i].events & ~POLLPRI;
+  adns__fdevents(ads,
+                pollfds,npollfds,
+                0,0,0,0,
+                now,&r);
+
+  adns__consistency(ads,0,cc_entex);
+  return 0;
+}
+
+void adns__autosys(adns_state ads, struct timeval now) {
+  if (ads->iflags & adns_if_noautosys) return;
+  adns_processany(ads);
+}
+
+int adns__internal_check(adns_state ads,
+                        adns_query *query_io,
+                        adns_answer **answer,
+                        void **context_r) {
+  adns_query qu;
+
+  qu= *query_io;
+  if (!qu) {
+    if (ads->output.head) {
+      qu= ads->output.head;
+    } else if (ads->udpw.head || ads->tcpw.head) {
+      return EAGAIN;
+    } else {
+      return ESRCH;
+    }
+  } else {
+    if (qu->id>=0) return EAGAIN;
+  }
+  LIST_UNLINK(ads->output,qu);
+  *answer= qu->answer;
+  if (context_r) *context_r= qu->ctx.ext;
+  *query_io= qu;
+  free(qu);
+  return 0;
+}
+
+int adns_wait(adns_state ads,
+             adns_query *query_io,
+             adns_answer **answer_r,
+             void **context_r) {
+  int r, maxfd, rsel;
+  fd_set readfds, writefds, exceptfds;
+  struct timeval tvbuf, *tvp;
+  
+  adns__consistency(ads,*query_io,cc_entex);
+  for (;;) {
+    r= adns__internal_check(ads,query_io,answer_r,context_r);
+    if (r != EAGAIN) break;
+    maxfd= 0; tvp= 0;
+    FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds);
+    adns_beforeselect(ads,&maxfd,&readfds,&writefds,&exceptfds,&tvp,&tvbuf,0);
+    assert(tvp);
+    rsel= select(maxfd,&readfds,&writefds,&exceptfds,tvp);
+    if (rsel==-1) {
+      if (errno == EINTR) {
+       if (ads->iflags & adns_if_eintr) { r= EINTR; break; }
+      } 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);
+    }
+  }
+  adns__consistency(ads,0,cc_entex);
+  return r;
+}
+
+int adns_check(adns_state ads,
+              adns_query *query_io,
+              adns_answer **answer_r,
+              void **context_r) {
+  struct timeval now;
+  int r;
+  
+  adns__consistency(ads,*query_io,cc_entex);
+  r= gettimeofday(&now,0);
+  if (!r) adns__autosys(ads,now);
+
+  r= adns__internal_check(ads,query_io,answer_r,context_r);
+  adns__consistency(ads,0,cc_entex);
+  return r;
+}
diff --git a/adns/src/general.c b/adns/src/general.c
new file mode 100644 (file)
index 0000000..0d159df
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * general.c
+ * - diagnostic functions
+ * - vbuf handling
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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 <stdlib.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "internal.h"
+
+/* Core diagnostic functions */
+
+void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
+                int serv, adns_query qu, const char *fmt, va_list al) {
+  const char *bef, *aft;
+  vbuf vb;
+  
+  if (!ads->diagfile ||
+      (!(ads->iflags & adns_if_debug) && (!prevent || (ads->iflags & prevent))))
+    return;
+
+  if (ads->iflags & adns_if_logpid) {
+    fprintf(ads->diagfile,"adns%s [%ld]: ",pfx,(long)getpid());
+  } else {
+    fprintf(ads->diagfile,"adns%s: ",pfx);
+  }
+
+  vfprintf(ads->diagfile,fmt,al);
+
+  bef= " (";
+  aft= "\n";
+
+  if (qu && qu->query_dgram) {
+    adns__vbuf_init(&vb);
+    fprintf(ads->diagfile,"%sQNAME=%s, QTYPE=%s",
+           bef,
+           adns__diag_domain(qu->ads,-1,0, &vb,
+                             qu->query_dgram,qu->query_dglen,DNS_HDRSIZE),
+           qu->typei ? qu->typei->rrtname : "<unknown>");
+    if (qu->typei && qu->typei->fmtname)
+      fprintf(ads->diagfile,"(%s)",qu->typei->fmtname);
+    bef=", "; aft=")\n";
+    adns__vbuf_free(&vb);
+  }
+  
+  if (serv>=0) {
+    fprintf(ads->diagfile,"%sNS=%s",bef,inet_ntoa(ads->servers[serv].addr));
+    bef=", "; aft=")\n";
+  }
+
+  fputs(aft,ads->diagfile);
+}
+
+void adns__debug(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
+  va_list al;
+
+  va_start(al,fmt);
+  adns__vdiag(ads," debug",0,serv,qu,fmt,al);
+  va_end(al);
+}
+
+void adns__warn(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
+  va_list al;
+
+  va_start(al,fmt);
+  adns__vdiag(ads," warning",adns_if_noerrprint|adns_if_noserverwarn,serv,qu,fmt,al);
+  va_end(al);
+}
+
+void adns__diag(adns_state ads, int serv, adns_query qu, const char *fmt, ...) {
+  va_list al;
+
+  va_start(al,fmt);
+  adns__vdiag(ads,"",adns_if_noerrprint,serv,qu,fmt,al);
+  va_end(al);
+}
+
+/* vbuf functions */
+
+void adns__vbuf_init(vbuf *vb) {
+  vb->used= vb->avail= 0; vb->buf= 0;
+}
+
+int adns__vbuf_ensure(vbuf *vb, int want) {
+  void *nb;
+  
+  if (vb->avail >= want) return 1;
+  nb= realloc(vb->buf,want); if (!nb) return 0;
+  vb->buf= nb;
+  vb->avail= want;
+  return 1;
+}
+  
+void adns__vbuf_appendq(vbuf *vb, const byte *data, int len) {
+  memcpy(vb->buf+vb->used,data,len);
+  vb->used+= len;
+}
+
+int adns__vbuf_append(vbuf *vb, const byte *data, int len) {
+  int newlen;
+  void *nb;
+
+  newlen= vb->used+len;
+  if (vb->avail < newlen) {
+    if (newlen<20) newlen= 20;
+    newlen <<= 1;
+    nb= realloc(vb->buf,newlen);
+    if (!nb) { newlen= vb->used+len; nb= realloc(vb->buf,newlen); }
+    if (!nb) return 0;
+    vb->buf= nb;
+    vb->avail= newlen;
+  }
+  adns__vbuf_appendq(vb,data,len);
+  return 1;
+}
+
+int adns__vbuf_appendstr(vbuf *vb, const char *data) {
+  int l;
+  l= strlen(data);
+  return adns__vbuf_append(vb,data,l);
+}
+
+void adns__vbuf_free(vbuf *vb) {
+  free(vb->buf);
+  adns__vbuf_init(vb);
+}
+
+/* Additional diagnostic functions */
+
+const char *adns__diag_domain(adns_state ads, int serv, adns_query qu,
+                             vbuf *vb, const byte *dgram, int dglen, int cbyte) {
+  adns_status st;
+
+  st= adns__parse_domain(ads,serv,qu,vb, pdf_quoteok, dgram,dglen,&cbyte,dglen);
+  if (st == adns_s_nomemory) {
+    return "<cannot report domain... out of memory>";
+  }
+  if (st) {
+    vb->used= 0;
+    if (!(adns__vbuf_appendstr(vb,"<bad format... ") &&
+         adns__vbuf_appendstr(vb,adns_strerror(st)) &&
+         adns__vbuf_appendstr(vb,">") &&
+         adns__vbuf_append(vb,"",1))) {
+      return "<cannot report bad format... out of memory>";
+    }
+  }
+  if (!vb->used) {
+    adns__vbuf_appendstr(vb,"<truncated ...>");
+    adns__vbuf_append(vb,"",1);
+  }
+  return vb->buf;
+}
+
+adns_status adns_rr_info(adns_rrtype type,
+                        const char **rrtname_r, const char **fmtname_r,
+                        int *len_r,
+                        const void *datap, char **data_r) {
+  const typeinfo *typei;
+  vbuf vb;
+  adns_status st;
+
+  typei= adns__findtype(type);
+  if (!typei) return adns_s_unknownrrtype;
+
+  if (rrtname_r) *rrtname_r= typei->rrtname;
+  if (fmtname_r) *fmtname_r= typei->fmtname;
+  if (len_r) *len_r= typei->rrsz;
+
+  if (!datap) return adns_s_ok;
+  
+  adns__vbuf_init(&vb);
+  st= typei->convstring(&vb,datap);
+  if (st) goto x_freevb;
+  if (!adns__vbuf_append(&vb,"",1)) { st= adns_s_nomemory; goto x_freevb; }
+  assert(strlen(vb.buf) == vb.used-1);
+  *data_r= realloc(vb.buf,vb.used);
+  if (!*data_r) *data_r= vb.buf;
+  return adns_s_ok;
+
+ x_freevb:
+  adns__vbuf_free(&vb);
+  return st;
+}
+
+
+#define SINFO(n,s) { adns_s_##n, #n, s }
+
+static const struct sinfo {
+  adns_status st;
+  const char *abbrev;
+  const char *string;
+} sinfos[]= {
+  SINFO(  ok,                  "OK"                                            ),
+
+  SINFO(  nomemory,            "Out of memory"                                 ),
+  SINFO(  unknownrrtype,       "Query not implemented in DNS library"          ),
+  SINFO(  systemfail,          "General resolver or system failure"            ),
+
+  SINFO(  timeout,             "DNS query timed out"                           ),
+  SINFO(  allservfail,         "All nameservers failed"                        ),
+  SINFO(  norecurse,           "Recursion denied by nameserver"                ),
+  SINFO(  invalidresponse,     "Nameserver sent bad response"                  ),
+  SINFO(  unknownformat,       "Nameserver used unknown format"                ),
+
+  SINFO(  rcodeservfail,       "Nameserver reports failure"                    ),
+  SINFO(  rcodeformaterror,    "Query not understood by nameserver"            ),
+  SINFO(  rcodenotimplemented, "Query not implemented by nameserver"           ),
+  SINFO(  rcoderefused,        "Query refused by nameserver"                   ),
+  SINFO(  rcodeunknown,        "Nameserver sent unknown response code"         ),
+  
+  SINFO(  inconsistent,        "Inconsistent resource records in DNS"          ),
+  SINFO(  prohibitedcname,     "DNS alias found where canonical name wanted"   ),
+  SINFO(  answerdomaininvalid, "Found syntactically invalid domain name"       ),
+  SINFO(  answerdomaintoolong, "Found overly-long domain name"                 ),
+  SINFO(  invaliddata,         "Found invalid DNS data"                        ),
+
+  SINFO(  querydomainwrong,    "Domain invalid for particular DNS query type"  ),
+  SINFO(  querydomaininvalid,  "Domain name is syntactically invalid"          ),
+  SINFO(  querydomaintoolong,  "Domain name or component is too long"          ),
+
+  SINFO(  nxdomain,            "No such domain"                                ),
+  SINFO(  nodata,              "No such data"                                  )
+};
+
+static int si_compar(const void *key, const void *elem) {
+  const adns_status *st= key;
+  const struct sinfo *si= elem;
+
+  return *st < si->st ? -1 : *st > si->st ? 1 : 0;
+}
+
+static const struct sinfo *findsinfo(adns_status st) {
+  return bsearch(&st,sinfos,sizeof(sinfos)/sizeof(*sinfos),sizeof(*sinfos),si_compar);
+}
+
+const char *adns_strerror(adns_status st) {
+  const struct sinfo *si;
+
+  si= findsinfo(st);
+  return si->string;
+}
+
+const char *adns_errabbrev(adns_status st) {
+  const struct sinfo *si;
+
+  si= findsinfo(st);
+  return si->abbrev;
+}
+
+
+#define STINFO(max) { adns_s_max_##max, #max }
+
+static const struct stinfo {
+  adns_status stmax;
+  const char *abbrev;
+} stinfos[]= {
+  { adns_s_ok, "ok" },
+  STINFO(  localfail   ),
+  STINFO(  remotefail  ),
+  STINFO(  tempfail    ),
+  STINFO(  misconfig   ),
+  STINFO(  misquery    ),
+  STINFO(  permfail    )
+};
+
+static int sti_compar(const void *key, const void *elem) {
+  const adns_status *st= key;
+  const struct stinfo *sti= elem;
+
+  adns_status here, min, max;
+
+  here= *st;
+  min= (sti==stinfos) ? 0 : sti[-1].stmax+1;
+  max= sti->stmax;
+  
+  return here < min  ? -1 : here > max ? 1 : 0;
+}
+
+const char *adns_errtypeabbrev(adns_status st) {
+  const struct stinfo *sti;
+
+  sti= bsearch(&st,stinfos,sizeof(stinfos)/sizeof(*stinfos),sizeof(*stinfos),sti_compar);
+  return sti->abbrev;
+}
+
+
+void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
+                int (*needswap)(void *context, const void *a, const void *b),
+                void *context) {
+  byte *data= array;
+  int i, place;
+
+  for (i=0; i<nobjs; i++) {
+    for (place= i;
+        place>0 && needswap(context, data + (place-1)*sz, data + i*sz);
+        place--);
+    if (place != i) {
+      memcpy(tempbuf, data + i*sz, sz);
+      memmove(data + (place+1)*sz, data + place*sz, (i-place)*sz);
+      memcpy(data + place*sz, tempbuf, sz);
+    }
+  }
+}
+
+/* SIGPIPE protection. */
+
+void adns__sigpipe_protect(adns_state ads) {
+  sigset_t toblock;
+  struct sigaction sa;
+  int r;
+
+  if (ads->iflags & adns_if_nosigpipe) return;
+
+  sigfillset(&toblock);
+  sigdelset(&toblock,SIGPIPE);
+
+  sa.sa_handler= SIG_IGN;
+  sigfillset(&sa.sa_mask);
+  sa.sa_flags= 0;
+  
+  r= sigprocmask(SIG_SETMASK,&toblock,&ads->stdsigmask); assert(!r);
+  r= sigaction(SIGPIPE,&sa,&ads->stdsigpipe); assert(!r);
+}
+
+void adns__sigpipe_unprotect(adns_state ads) {
+  int r;
+
+  if (ads->iflags & adns_if_nosigpipe) return;
+
+  r= sigaction(SIGPIPE,&ads->stdsigpipe,0); assert(!r);
+  r= sigprocmask(SIG_SETMASK,&ads->stdsigmask,0); assert(!r);
+}
diff --git a/adns/src/internal.h b/adns/src/internal.h
new file mode 100644 (file)
index 0000000..4941305
--- /dev/null
@@ -0,0 +1,715 @@
+/*
+ * internal.h
+ * - declarations of private objects with external linkage (adns__*)
+ * - definitons of internal macros
+ * - comments regarding library data structures
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *
+ *  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.
+ */
+
+#ifndef ADNS_INTERNAL_H_INCLUDED
+#define ADNS_INTERNAL_H_INCLUDED
+
+#include "config.h"
+typedef unsigned char byte;
+
+#include <stdarg.h>
+#include <assert.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/time.h>
+
+#include "adns.h"
+#include "dlist.h"
+
+#ifdef ADNS_REGRESS_TEST
+# include "hredirect.h"
+#endif
+
+/* Configuration and constants */
+
+#define MAXSERVERS 5
+#define MAXSORTLIST 15
+#define UDPMAXRETRIES 15
+#define UDPRETRYMS 2000
+#define TCPWAITMS 30000
+#define TCPCONNMS 14000
+#define TCPIDLEMS 30000
+#define MAXTTLBELIEVE (7*86400) /* any TTL > 7 days is capped */
+
+#define DNS_PORT 53
+#define DNS_MAXUDP 512
+#define DNS_MAXLABEL 63
+#define DNS_MAXDOMAIN 255
+#define DNS_HDRSIZE 12
+#define DNS_IDOFFSET 0
+#define DNS_CLASS_IN 1
+
+#define DNS_INADDR_ARPA "in-addr", "arpa"
+
+#define MAX_POLLFDS  ADNS_POLLFDS_RECOMMENDED
+
+typedef enum {
+  cc_user,
+  cc_entex,
+  cc_freq
+} consistency_checks;
+
+typedef enum {
+  rcode_noerror,
+  rcode_formaterror,
+  rcode_servfail,
+  rcode_nxdomain,
+  rcode_notimp,
+  rcode_refused
+} dns_rcode;
+
+/* Shared data structures */
+
+typedef union {
+  adns_status status;
+  char *cp;
+  adns_rrtype type;
+  int i;
+  struct in_addr ia;
+  unsigned long ul;
+} rr_align;
+
+typedef struct {
+  int used, avail;
+  byte *buf;
+} vbuf;
+
+typedef struct {
+  adns_state ads;
+  adns_query qu;
+  int serv;
+  const byte *dgram;
+  int dglen, nsstart, nscount, arcount;
+  struct timeval now;
+} parseinfo;
+
+typedef struct {
+  adns_rrtype type;
+  const char *rrtname;
+  const char *fmtname;
+  int rrsz;
+
+  void (*makefinal)(adns_query qu, void *data);
+  /* Change memory management of *data.
+   * Previously, used alloc_interim, now use alloc_final.
+   */
+
+  adns_status (*convstring)(vbuf *vb, const void *data);
+  /* Converts the RR data to a string representation in vbuf.
+   * vbuf will be appended to (it must have been initialised),
+   * and will not be null-terminated by convstring.
+   */
+
+  adns_status (*parse)(const parseinfo *pai, int cbyte, int max, void *store_r);
+  /* Parse one RR, in dgram of length dglen, starting at cbyte and
+   * extending until at most max.
+   *
+   * The RR should be stored at *store_r, of length qu->typei->rrsz.
+   *
+   * If there is an overrun which might indicate truncation, it should set
+   * *rdstart to -1; otherwise it may set it to anything else positive.
+   *
+   * nsstart is the offset of the authority section.
+   */
+
+  int (*diff_needswap)(adns_state ads, const void *datap_a, const void *datap_b);
+  /* Returns !0 if RR a should be strictly after RR b in the sort order,
+   * 0 otherwise.  Must not fail.
+   */
+} typeinfo;
+
+typedef struct allocnode {
+  struct allocnode *next, *back;
+} allocnode;
+
+union maxalign {
+  byte d[1];
+  struct in_addr ia;
+  long l;
+  void *p;
+  void (*fp)(void);
+  union maxalign *up;
+} data;
+
+typedef struct {
+  void *ext;
+  void (*callback)(adns_query parent, adns_query child);
+  union {
+    adns_rr_addr ptr_parent_addr;
+    adns_rr_hostaddr *hostaddr;
+  } info;
+} qcontext;
+
+struct adns__query {
+  adns_state ads;
+  enum { query_tosend, query_tcpw, query_childw, query_done } state;
+  adns_query back, next, parent;
+  struct { adns_query head, tail; } children;
+  struct { adns_query back, next; } siblings;
+  struct { allocnode *head, *tail; } allocations;
+  int interim_allocd, preserved_allocd;
+  void *final_allocspace;
+
+  const typeinfo *typei;
+  byte *query_dgram;
+  int query_dglen;
+
+  vbuf vb;
+  /* General-purpose messing-about buffer.
+   * Wherever a `big' interface is crossed, this may be corrupted/changed
+   * unless otherwise specified.
+   */
+
+  adns_answer *answer;
+  /* This is allocated when a query is submitted, to avoid being unable
+   * to relate errors to queries if we run out of memory.  During
+   * query processing status, rrs is 0.  cname is set if
+   * we found a cname (this corresponds to cname_dgram in the query
+   * structure).  type is set from the word go.  nrrs and rrs
+   * are set together, when we find how many rrs there are.
+   * owner is set during querying unless we're doing searchlist,
+   * in which case it is set only when we find an answer.
+   */
+
+  byte *cname_dgram;
+  int cname_dglen, cname_begin;
+  /* If non-0, has been allocated using . */
+
+  vbuf search_vb;
+  int search_origlen, search_pos, search_doneabs;
+  /* Used by the searching algorithm.  The query domain in textual form
+   * is copied into the vbuf, and _origlen set to its length.  Then
+   * we walk the searchlist, if we want to.  _pos says where we are
+   * (next entry to try), and _doneabs says whether we've done the
+   * absolute query yet (0=not yet, 1=done, -1=must do straight away,
+   * but not done yet).  If flags doesn't have adns_qf_search then
+   * the vbuf is initialised but empty and everything else is zero.
+   */
+
+  int id, flags, retries;
+  int udpnextserver;
+  unsigned long udpsent; /* bitmap indexed by server */
+  struct timeval timeout;
+  time_t expires; /* Earliest expiry time of any record we used. */
+
+  qcontext ctx;
+
+  /* Possible states:
+   *
+   *  state   Queue   child  id   nextudpserver  udpsent     tcpfailed
+   *
+   *  tosend  NONE    null   >=0  0              zero        zero
+   *  tosend  udpw    null   >=0  any            nonzero     zero
+   *  tosend  NONE    null   >=0  any            nonzero     zero
+   *
+   *  tcpw    tcpw    null   >=0  irrelevant     any         any
+   *
+   *  child   childw  set    >=0  irrelevant     irrelevant  irrelevant
+   *  child   NONE    null   >=0  irrelevant     irrelevant  irrelevant
+   *  done    output  null   -1   irrelevant     irrelevant  irrelevant
+   *
+   * Queries are only not on a queue when they are actually being processed.
+   * Queries in state tcpw/tcpw have been sent (or are in the to-send buffer)
+   * iff the tcp connection is in state server_ok.
+   *
+   *                         +------------------------+
+   *             START -----> |      tosend/NONE       |
+   *                         +------------------------+
+   *                         /                       |\  \
+   *        too big for UDP /             UDP timeout  \  \ send via UDP
+   *        send via TCP   /              more retries  \  \
+   *        when conn'd   /                  desired     \  \
+   *                     |                                       |  |
+   *                     v                               |  v
+   *              +-----------+                        +-------------+
+   *              | tcpw/tcpw | ________                | tosend/udpw |
+   *              +-----------+         \              +-------------+
+   *                 |    |              |     UDP timeout | |
+   *                 |    |              |      no more    | |
+   *                 |    |              |      retries    | |
+   *                  \   | TCP died     |      desired    | |
+   *                   \   \ no more     |                 | |
+   *                    \   \ servers    | TCP            /  |
+   *                     \   \ to try    | timeout       /   |
+   *                  got \   \          v             |_    | got
+   *                 reply \   _| +------------------+      / reply
+   *                               \     | done/output FAIL |     /
+   *                         \    +------------------+    /
+   *                          \                          /
+   *                           _|                      |_
+   *                             (..... got reply ....)
+   *                              /                   \
+   *        need child query/ies /                     \ no child query
+   *                            /                       \
+   *                          |_                         _|
+   *              +---------------+                   +----------------+
+   *               | childw/childw | ----------------> | done/output OK |
+   *               +---------------+  children done    +----------------+
+   */
+
+   void (*callback) (adns_state, adns_query, void *);
+};
+
+struct query_queue { adns_query head, tail; };
+
+struct adns__state {
+  adns_initflags iflags;
+  FILE *diagfile;
+  int configerrno;
+  struct query_queue udpw, tcpw, childw, output;
+  adns_query forallnext;
+  int nextid, udpsocket, tcpsocket;
+  vbuf tcpsend, tcprecv;
+  int nservers, nsortlist, nsearchlist, searchndots, tcpserver, tcprecv_skip;
+  enum adns__tcpstate {
+    server_disconnected, server_connecting,
+    server_ok, server_broken
+  } tcpstate;
+  struct timeval tcptimeout;
+  /* This will have tv_sec==0 if it is not valid.  It will always be
+   * valid if tcpstate _connecting.  When _ok, it will be nonzero if
+   * we are idle (ie, tcpw queue is empty), in which case it is the
+   * absolute time when we will close the connection.
+   */
+  struct sigaction stdsigpipe;
+  sigset_t stdsigmask;
+  struct pollfd pollfds_buf[MAX_POLLFDS];
+  struct server {
+    struct in_addr addr;
+  } servers[MAXSERVERS];
+  struct sortlist {
+    struct in_addr base, mask;
+  } sortlist[MAXSORTLIST];
+  char **searchlist;
+};
+
+/* From setup.c: */
+
+int adns__setnonblock(adns_state ads, int fd); /* => errno value */
+
+/* From general.c: */
+
+void adns__vdiag(adns_state ads, const char *pfx, adns_initflags prevent,
+                int serv, adns_query qu, const char *fmt, va_list al);
+
+void adns__debug(adns_state ads, int serv, adns_query qu,
+                const char *fmt, ...) PRINTFFORMAT(4,5);
+void adns__warn(adns_state ads, int serv, adns_query qu,
+               const char *fmt, ...) PRINTFFORMAT(4,5);
+void adns__diag(adns_state ads, int serv, adns_query qu,
+               const char *fmt, ...) PRINTFFORMAT(4,5);
+
+int adns__vbuf_ensure(vbuf *vb, int want);
+int adns__vbuf_appendstr(vbuf *vb, const char *data); /* does not include nul */
+int adns__vbuf_append(vbuf *vb, const byte *data, int len);
+/* 1=>success, 0=>realloc failed */
+void adns__vbuf_appendq(vbuf *vb, const byte *data, int len);
+void adns__vbuf_init(vbuf *vb);
+void adns__vbuf_free(vbuf *vb);
+
+const char *adns__diag_domain(adns_state ads, int serv, adns_query qu,
+                             vbuf *vb, const byte *dgram, int dglen, int cbyte);
+/* Unpicks a domain in a datagram and returns a string suitable for
+ * printing it as.  Never fails - if an error occurs, it will
+ * return some kind of string describing the error.
+ *
+ * serv may be -1 and qu may be 0.  vb must have been initialised,
+ * and will be left in an arbitrary consistent state.
+ *
+ * Returns either vb->buf, or a pointer to a string literal.  Do not modify
+ * vb before using the return value.
+ */
+
+void adns__isort(void *array, int nobjs, int sz, void *tempbuf,
+                int (*needswap)(void *context, const void *a, const void *b),
+                void *context);
+/* Does an insertion sort of array which must contain nobjs objects
+ * each sz bytes long.  tempbuf must point to a buffer at least
+ * sz bytes long.  needswap should return !0 if a>b (strictly, ie
+ * wrong order) 0 if a<=b (ie, order is fine).
+ */
+
+void adns__sigpipe_protect(adns_state);
+void adns__sigpipe_unprotect(adns_state);
+/* If SIGPIPE protection is not disabled, will block all signals except
+ * SIGPIPE, and set SIGPIPE's disposition to SIG_IGN.  (And then restore.)
+ * Each call to _protect must be followed by a call to _unprotect before
+ * any significant amount of code gets to run, since the old signal mask
+ * is stored in the adns structure.
+ */
+
+/* From transmit.c: */
+
+adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
+                         const char *owner, int ol,
+                         const typeinfo *typei, adns_queryflags flags);
+/* Assembles a query packet in vb.  A new id is allocated and returned.
+ */
+
+adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
+                                 const byte *qd_dgram, int qd_dglen, int qd_begin,
+                                 adns_rrtype type, adns_queryflags flags);
+/* Same as adns__mkquery, but takes the owner domain from an existing datagram.
+ * That domain must be correct and untruncated.
+ */
+
+void adns__querysend_tcp(adns_query qu, struct timeval now);
+/* Query must be in state tcpw/tcpw; it will be sent if possible and
+ * no further processing can be done on it for now.  The connection
+ * might be broken, but no reconnect will be attempted.
+ */
+
+void adns__query_send(adns_query qu, struct timeval now);
+/* Query must be in state tosend/NONE; it will be moved to a new state,
+ * and no further processing can be done on it for now.
+ * (Resulting state is one of udp/timew, tcpwait/timew (if server not connected),
+ *  tcpsent/timew, child/childw or done/output.)
+ * __query_send may decide to use either UDP or TCP depending whether
+ * _qf_usevc is set (or has become set) and whether the query is too
+ * large.
+ */
+
+/* From query.c: */
+
+adns_status adns__internal_submit(adns_state ads, adns_query *query_r,
+                                 const typeinfo *typei, vbuf *qumsg_vb, int id,
+                                 adns_queryflags flags, struct timeval now,
+                                 const qcontext *ctx);
+/* Submits a query (for internal use, called during external submits).
+ *
+ * The new query is returned in *query_r, or we return adns_s_nomemory.
+ *
+ * The query datagram should already have been assembled in qumsg_vb;
+ * the memory for it is _taken over_ by this routine whether it
+ * succeeds or fails (if it succeeds, the vbuf is reused for qu->vb).
+ *
+ * *ctx is copied byte-for-byte into the query.
+ *
+ * When the child query is done, ctx->callback will be called.  The
+ * child will already have been taken off both the global list of
+ * queries in ads and the list of children in the parent.  The child
+ * will be freed when the callback returns.  The parent will have been
+ * taken off the global childw queue.
+ *
+ * The callback should either call adns__query_done, if it is
+ * complete, or adns__query_fail, if an error has occurred, in which
+ * case the other children (if any) will be cancelled.  If the parent
+ * has more unfinished children (or has just submitted more) then the
+ * callback may choose to wait for them - it must then put the parent
+ * back on the childw queue.
+ */
+
+void adns__search_next(adns_state ads, adns_query qu, struct timeval now);
+/* Walks down the searchlist for a query with adns_qf_search.
+ * The query should have just had a negative response, or not had
+ * any queries sent yet, and should not be on any queue.
+ * The query_dgram if any will be freed and forgotten and a new
+ * one constructed from the search_* members of the query.
+ *
+ * Cannot fail (in case of error, calls adns__query_fail).
+ */
+
+void *adns__alloc_interim(adns_query qu, size_t sz);
+void *adns__alloc_preserved(adns_query qu, size_t sz);
+/* Allocates some memory, and records which query it came from
+ * and how much there was.
+ *
+ * If an error occurs in the query, all the memory from _interim is
+ * simply freed.  If the query succeeds, one large buffer will be made
+ * which is big enough for all these allocations, and then
+ * adns__alloc_final will get memory from this buffer.
+ *
+ * _alloc_interim can fail (and return 0).
+ * The caller must ensure that the query is failed.
+ *
+ * The memory from _preserved is is kept and transferred into the
+ * larger buffer - unless we run out of memory, in which case it too
+ * is freed.  When you use _preserved you have to add code to the
+ * x_nomem error exit case in adns__makefinal_query to clear out the
+ * pointers you made to those allocations, because that's when they're
+ * thrown away; you should also make a note in the declaration of
+ * those pointer variables, to note that they are _preserved rather
+ * than _interim.  If they're in the answer, note it here:
+ *  answer->cname and answer->owner are _preserved.
+ */
+
+void adns__transfer_interim(adns_query from, adns_query to, void *block, size_t sz);
+/* Transfers an interim allocation from one query to another, so that
+ * the `to' query will have room for the data when we get to makefinal
+ * and so that the free will happen when the `to' query is freed
+ * rather than the `from' query.
+ *
+ * It is legal to call adns__transfer_interim with a null pointer; this
+ * has no effect.
+ *
+ * _transfer_interim also ensures that the expiry time of the `to' query
+ * is no later than that of the `from' query, so that child queries'
+ * TTLs get inherited by their parents.
+ */
+
+void *adns__alloc_mine(adns_query qu, size_t sz);
+/* Like _interim, but does not record the length for later
+ * copying into the answer.  This just ensures that the memory
+ * will be freed when we're done with the query.
+ */
+
+void *adns__alloc_final(adns_query qu, size_t sz);
+/* Cannot fail, and cannot return 0.
+ */
+
+void adns__makefinal_block(adns_query qu, void **blpp, size_t sz);
+void adns__makefinal_str(adns_query qu, char **strp);
+
+void adns__reset_preserved(adns_query qu);
+/* Resets all of the memory management stuff etc. to take account of
+ * only the _preserved stuff from _alloc_preserved.  Used when we find
+ * an error somewhere and want to just report the error (with perhaps
+ * CNAME, owner, etc. info), and also when we're halfway through RRs
+ * in a datagram and discover that we need to retry the query.
+ */
+
+void adns__query_done(adns_query qu);
+void adns__query_fail(adns_query qu, adns_status stat);
+
+/* From reply.c: */
+
+void adns__procdgram(adns_state ads, const byte *dgram, int len,
+                    int serv, int viatcp, struct timeval now);
+/* This function is allowed to cause new datagrams to be constructed
+ * and sent, or even new queries to be started.  However,
+ * query-sending functions are not allowed to call any general event
+ * loop functions in case they accidentally call this.
+ *
+ * Ie, receiving functions may call sending functions.
+ * Sending functions may NOT call receiving functions.
+ */
+
+/* From types.c: */
+
+const typeinfo *adns__findtype(adns_rrtype type);
+
+/* From parse.c: */
+
+typedef struct {
+  adns_state ads;
+  adns_query qu;
+  int serv;
+  const byte *dgram;
+  int dglen, max, cbyte, namelen;
+  int *dmend_r;
+} findlabel_state;
+
+void adns__findlabel_start(findlabel_state *fls, adns_state ads,
+                          int serv, adns_query qu,
+                          const byte *dgram, int dglen, int max,
+                          int dmbegin, int *dmend_rlater);
+/* Finds labels in a domain in a datagram.
+ *
+ * Call this routine first.
+ * dmend_rlater may be null.  ads (and of course fls) may not be.
+ * serv may be -1, qu may be null - they are for error reporting.
+ */
+
+adns_status adns__findlabel_next(findlabel_state *fls, int *lablen_r, int *labstart_r);
+/* Then, call this one repeatedly.
+ *
+ * It will return adns_s_ok if all is well, and tell you the length
+ * and start of successive labels.  labstart_r may be null, but
+ * lablen_r must not be.
+ *
+ * After the last label, it will return with *lablen_r zero.
+ * Do not then call it again; instead, just throw away the findlabel_state.
+ *
+ * *dmend_rlater will have been set to point to the next part of
+ * the datagram after the label (or after the uncompressed part,
+ * if compression was used).  *namelen_rlater will have been set
+ * to the length of the domain name (total length of labels plus
+ * 1 for each intervening dot).
+ *
+ * If the datagram appears to be truncated, *lablen_r will be -1.
+ * *dmend_rlater, *labstart_r and *namelen_r may contain garbage.
+ * Do not call _next again.
+ *
+ * There may also be errors, in which case *dmend_rlater,
+ * *namelen_rlater, *lablen_r and *labstart_r may contain garbage.
+ * Do not then call findlabel_next again.
+ */
+
+typedef enum {
+  pdf_quoteok= 0x001
+} parsedomain_flags;
+
+adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu,
+                              vbuf *vb, parsedomain_flags flags,
+                              const byte *dgram, int dglen, int *cbyte_io, int max);
+/* vb must already have been initialised; it will be reset if necessary.
+ * If there is truncation, vb->used will be set to 0; otherwise
+ * (if there is no error) vb will be null-terminated.
+ * If there is an error vb and *cbyte_io may be left indeterminate.
+ *
+ * serv may be -1 and qu may be 0 - they are used for error reporting only.
+ */
+
+adns_status adns__parse_domain_more(findlabel_state *fls, adns_state ads,
+                                   adns_query qu, vbuf *vb, parsedomain_flags flags,
+                                   const byte *dgram);
+/* Like adns__parse_domain, but you pass it a pre-initialised findlabel_state,
+ * for continuing an existing domain or some such of some kind.  Also, unlike
+ * _parse_domain, the domain data will be appended to vb, rather than replacing
+ * the existing contents.
+ */
+
+adns_status adns__findrr(adns_query qu, int serv,
+                        const byte *dgram, int dglen, int *cbyte_io,
+                        int *type_r, int *class_r, unsigned long *ttl_r,
+                        int *rdlen_r, int *rdstart_r,
+                        int *ownermatchedquery_r);
+/* Finds the extent and some of the contents of an RR in a datagram
+ * and does some checks.  The datagram is *dgram, length dglen, and
+ * the RR starts at *cbyte_io (which is updated afterwards to point
+ * to the end of the RR).
+ *
+ * The type, class, TTL and RRdata length and start are returned iff
+ * the corresponding pointer variables are not null.  type_r, class_r
+ * and ttl_r may not be null.  The TTL will be capped.
+ *
+ * If ownermatchedquery_r != 0 then the owner domain of this
+ * RR will be compared with that in the query (or, if the query
+ * has gone to a CNAME lookup, with the canonical name).
+ * In this case, *ownermatchedquery_r will be set to 0 or 1.
+ * The query datagram (or CNAME datagram) MUST be valid and not truncated.
+ *
+ * If there is truncation then *type_r will be set to -1 and
+ * *cbyte_io, *class_r, *rdlen_r, *rdstart_r and *eo_matched_r will be
+ * undefined.
+ *
+ * qu must obviously be non-null.
+ *
+ * If an error is returned then *type_r will be undefined too.
+ */
+
+adns_status adns__findrr_anychk(adns_query qu, int serv,
+                               const byte *dgram, int dglen, int *cbyte_io,
+                               int *type_r, int *class_r, unsigned long *ttl_r,
+                               int *rdlen_r, int *rdstart_r,
+                               const byte *eo_dgram, int eo_dglen, int eo_cbyte,
+                               int *eo_matched_r);
+/* Like adns__findrr_checked, except that the datagram and
+ * owner to compare with can be specified explicitly.
+ *
+ * If the caller thinks they know what the owner of the RR ought to
+ * be they can pass in details in eo_*: this is another (or perhaps
+ * the same datagram), and a pointer to where the putative owner
+ * starts in that datagram.  In this case *eo_matched_r will be set
+ * to 1 if the datagram matched or 0 if it did not.  Either
+ * both eo_dgram and eo_matched_r must both be non-null, or they
+ * must both be null (in which case eo_dglen and eo_cbyte will be ignored).
+ * The eo datagram and contained owner domain MUST be valid and
+ * untruncated.
+ */
+
+void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now);
+/* Updates the `expires' field in the query, so that it doesn't exceed
+ * now + ttl.
+ */
+
+int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len);
+
+/* From event.c: */
+
+void adns__tcp_broken(adns_state ads, const char *what, const char *why);
+/* what and why may be both 0, or both non-0. */
+
+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.
+ * Must not be called from within adns internal processing functions,
+ * lest we end up in recursive descent !
+ */
+
+void adns__must_gettimeofday(adns_state ads, const struct timeval **now_io,
+                            struct timeval *tv_buf);
+
+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);
+int adns__internal_check(adns_state ads,
+                        adns_query *query_io,
+                        adns_answer **answer,
+                        void **context_r);
+
+void adns__timeouts(adns_state ads, int act,
+                   struct timeval **tv_io, struct timeval *tvbuf,
+                   struct timeval now);
+/* If act is !0, then this will also deal with the TCP connection
+ * if previous events broke it or require it to be connected.
+ */
+
+/* From check.c: */
+
+void adns__consistency(adns_state ads, adns_query qu, consistency_checks cc);
+
+/* Useful static inline functions: */
+
+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');
+}
+static inline int ctype_822special(int c) { return strchr("()<>@,;:\\\".[]",c) != 0; }
+static inline int ctype_domainunquoted(int c) {
+  return ctype_alpha(c) || ctype_digit(c) || (strchr("-_/+",c) != 0);
+}
+
+static inline int errno_resources(int e) { return e==ENOMEM || e==ENOBUFS; }
+
+/* Useful macros */
+
+#define MEM_ROUND(sz) \
+  (( ((sz)+sizeof(union maxalign)-1) / sizeof(union maxalign) ) \
+   * sizeof(union maxalign) )
+
+#define GETIL_B(cb) (((dgram)[(cb)++]) & 0x0ff)
+#define GET_B(cb,tv) ((tv)= GETIL_B((cb)))
+#define GET_W(cb,tv) ((tv)=0, (tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb), (tv))
+#define GET_L(cb,tv) ( (tv)=0, \
+                      (tv)|=(GETIL_B((cb))<<24), \
+                      (tv)|=(GETIL_B((cb))<<16), \
+                      (tv)|=(GETIL_B((cb))<<8), \
+                      (tv)|=GETIL_B(cb), \
+                      (tv) )
+
+#endif
diff --git a/adns/src/parse.c b/adns/src/parse.c
new file mode 100644 (file)
index 0000000..73f53f7
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * parse.c
+ * - parsing assistance functions (mainly for domains inside datagrams)
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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 "internal.h"
+
+int vbuf__append_quoted1035(vbuf *vb, const byte *buf, int len) {
+  char qbuf[10];
+  int i, ch;
+  
+  while (len) {
+    qbuf[0]= 0;
+    for (i=0; i<len; i++) {
+      ch= buf[i];
+      if (ch <= ' ' || ch >= 127) {
+       sprintf(qbuf,"\\%03o",ch);
+       break;
+      } else if (!ctype_domainunquoted(ch)) {
+       sprintf(qbuf,"\\%c",ch);
+       break;
+      }
+    }
+    if (!adns__vbuf_append(vb,buf,i) || !adns__vbuf_append(vb,qbuf,strlen(qbuf)))
+      return 0;
+    if (i<len) i++;
+    buf+= i;
+    len-= i;
+  }
+  return 1;
+}
+
+void adns__findlabel_start(findlabel_state *fls, adns_state ads,
+                          int serv, adns_query qu,
+                          const byte *dgram, int dglen, int max,
+                          int dmbegin, int *dmend_rlater) {
+  fls->ads= ads;
+  fls->qu= qu;
+  fls->serv= serv;
+  fls->dgram= dgram;
+  fls->dglen= dglen;
+  fls->max= max;
+  fls->cbyte= dmbegin;
+  fls->namelen= 0;
+  fls->dmend_r= dmend_rlater;
+}
+
+adns_status adns__findlabel_next(findlabel_state *fls,
+                                int *lablen_r, int *labstart_r) {
+  int lablen, jumpto;
+  const char *dgram;
+
+  dgram= fls->dgram;
+  for (;;) {
+    if (fls->cbyte >= fls->dglen) goto x_truncated;
+    if (fls->cbyte >= fls->max) goto x_badresponse;
+    GET_B(fls->cbyte,lablen);
+    if (!(lablen & 0x0c0)) break;
+    if ((lablen & 0x0c0) != 0x0c0) return adns_s_unknownformat;
+    if (fls->cbyte >= fls->dglen) goto x_truncated;
+    if (fls->cbyte >= fls->max) goto x_badresponse;
+    GET_B(fls->cbyte,jumpto);
+    jumpto |= (lablen&0x3f)<<8;
+    if (fls->dmend_r) *(fls->dmend_r)= fls->cbyte;
+    fls->cbyte= jumpto;
+    fls->dmend_r= 0; fls->max= fls->dglen+1;
+  }
+  if (labstart_r) *labstart_r= fls->cbyte;
+  if (lablen) {
+    if (fls->namelen) fls->namelen++;
+    fls->namelen+= lablen;
+    if (fls->namelen > DNS_MAXDOMAIN) return adns_s_answerdomaintoolong;
+    fls->cbyte+= lablen;
+    if (fls->cbyte > fls->dglen) goto x_truncated;
+    if (fls->cbyte > fls->max) goto x_badresponse;
+  } else {
+    if (fls->dmend_r) *(fls->dmend_r)= fls->cbyte;
+  }
+  *lablen_r= lablen;
+  return adns_s_ok;
+
+ x_truncated:
+  *lablen_r= -1;
+  return adns_s_ok;
+
+ x_badresponse: 
+  adns__diag(fls->ads,fls->serv,fls->qu,"label in domain runs beyond end of domain");
+  return adns_s_invalidresponse;
+}
+
+adns_status adns__parse_domain(adns_state ads, int serv, adns_query qu,
+                              vbuf *vb, adns_queryflags flags,
+                              const byte *dgram, int dglen, int *cbyte_io, int max) {
+  findlabel_state fls;
+  
+  adns__findlabel_start(&fls,ads, serv,qu, dgram,dglen,max, *cbyte_io,cbyte_io);
+  vb->used= 0;
+  return adns__parse_domain_more(&fls,ads,qu, vb,flags,dgram);
+}
+
+adns_status adns__parse_domain_more(findlabel_state *fls, adns_state ads,
+                                   adns_query qu, vbuf *vb, parsedomain_flags flags,
+                                   const byte *dgram) {
+  int lablen, labstart, i, ch, first;
+  adns_status st;
+
+  first= 1;
+  for (;;) {
+    st= adns__findlabel_next(fls,&lablen,&labstart);
+    if (st) return st;
+    if (lablen<0) { vb->used=0; return adns_s_ok; }
+    if (!lablen) break;
+    if (first) {
+      first= 0;
+    } else {
+      if (!adns__vbuf_append(vb,".",1)) return adns_s_nomemory;
+    }
+    if (flags & pdf_quoteok) {
+      if (!vbuf__append_quoted1035(vb,dgram+labstart,lablen))
+       return adns_s_nomemory;
+    } else {
+      ch= dgram[labstart];
+      if (!ctype_alpha(ch) && !ctype_digit(ch)) return adns_s_answerdomaininvalid;
+      for (i= labstart+1; i<labstart+lablen; i++) {
+       ch= dgram[i];
+       if (ch != '-' && !ctype_alpha(ch) && !ctype_digit(ch))
+         return adns_s_answerdomaininvalid;
+      }
+      if (!adns__vbuf_append(vb,dgram+labstart,lablen))
+       return adns_s_nomemory;
+    }
+  }
+  if (!adns__vbuf_append(vb,"",1)) return adns_s_nomemory;
+  return adns_s_ok;
+}
+       
+adns_status adns__findrr_anychk(adns_query qu, int serv,
+                               const byte *dgram, int dglen, int *cbyte_io,
+                               int *type_r, int *class_r, unsigned long *ttl_r,
+                               int *rdlen_r, int *rdstart_r,
+                               const byte *eo_dgram, int eo_dglen, int eo_cbyte,
+                               int *eo_matched_r) {
+  findlabel_state fls, eo_fls;
+  int cbyte;
+  
+  int tmp, rdlen, mismatch;
+  unsigned long ttl;
+  int lablen, labstart, ch;
+  int eo_lablen, eo_labstart, eo_ch;
+  adns_status st;
+
+  cbyte= *cbyte_io;
+
+  adns__findlabel_start(&fls,qu->ads, serv,qu, dgram,dglen,dglen,cbyte,&cbyte);
+  if (eo_dgram) {
+    adns__findlabel_start(&eo_fls,qu->ads, -1,0, eo_dgram,eo_dglen,eo_dglen,eo_cbyte,0);
+    mismatch= 0;
+  } else {
+    mismatch= 1;
+  }
+  
+  for (;;) {
+    st= adns__findlabel_next(&fls,&lablen,&labstart);
+    if (st) return st;
+    if (lablen<0) goto x_truncated;
+
+    if (!mismatch) {
+      st= adns__findlabel_next(&eo_fls,&eo_lablen,&eo_labstart);
+      assert(!st); assert(eo_lablen>=0);
+      if (lablen != eo_lablen) mismatch= 1;
+      while (!mismatch && eo_lablen-- > 0) {
+       ch= dgram[labstart++]; if (ctype_alpha(ch)) ch &= ~32;
+       eo_ch= eo_dgram[eo_labstart++]; if (ctype_alpha(eo_ch)) eo_ch &= ~32;
+       if (ch != eo_ch) mismatch= 1;
+      }
+    }
+    if (!lablen) break;
+  }
+  if (eo_matched_r) *eo_matched_r= !mismatch;
+   
+  if (cbyte+10>dglen) goto x_truncated;
+  GET_W(cbyte,tmp); *type_r= tmp;
+  GET_W(cbyte,tmp); *class_r= tmp;
+
+  GET_L(cbyte,ttl);
+  if (ttl > MAXTTLBELIEVE) ttl= MAXTTLBELIEVE;
+  *ttl_r= ttl;
+  
+  GET_W(cbyte,rdlen); if (rdlen_r) *rdlen_r= rdlen;
+  if (rdstart_r) *rdstart_r= cbyte;
+  cbyte+= rdlen;
+  if (cbyte>dglen) goto x_truncated;
+  *cbyte_io= cbyte;
+  return adns_s_ok;
+
+ x_truncated:
+  *type_r= -1;
+  return 0;
+}
+
+adns_status adns__findrr(adns_query qu, int serv,
+                        const byte *dgram, int dglen, int *cbyte_io,
+                        int *type_r, int *class_r, unsigned long *ttl_r,
+                        int *rdlen_r, int *rdstart_r,
+                        int *ownermatchedquery_r) {
+  if (!ownermatchedquery_r) {
+    return adns__findrr_anychk(qu,serv,
+                              dgram,dglen,cbyte_io,
+                              type_r,class_r,ttl_r,rdlen_r,rdstart_r,
+                              0,0,0, 0);
+  } else if (!qu->cname_dgram) {
+    return adns__findrr_anychk(qu,serv,
+                              dgram,dglen,cbyte_io,
+                              type_r,class_r,ttl_r,rdlen_r,rdstart_r,
+                              qu->query_dgram,qu->query_dglen,DNS_HDRSIZE,
+                              ownermatchedquery_r);
+  } else {
+    return adns__findrr_anychk(qu,serv,
+                              dgram,dglen,cbyte_io,
+                              type_r,class_r,ttl_r,rdlen_r,rdstart_r,
+                              qu->cname_dgram,qu->cname_dglen,qu->cname_begin,
+                              ownermatchedquery_r);
+  }
+}
diff --git a/adns/src/poll.c b/adns/src/poll.c
new file mode 100644 (file)
index 0000000..63de431
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * poll.c
+ * - wrappers for poll(2)
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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 <string.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, r;
+  struct pollfd fds_tmp[MAX_POLLFDS];
+
+  adns__consistency(ads,0,cc_entex);
+
+  if (timeout_io) {
+    adns__must_gettimeofday(ads,&now,&tv_nowbuf);
+    if (!now) { *nfds_io= 0; r= 0; goto xit; }
+
+    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, 0, &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 (space < found) { r= ERANGE; goto xit; }
+    memcpy(fds,fds_tmp,sizeof(struct pollfd)*found);
+  }
+  r= 0;
+xit:
+  adns__consistency(ads,0,cc_entex);
+  return r;
+}
+
+void adns_afterpoll(adns_state ads, const struct pollfd *fds, int nfds,
+                   const struct timeval *now) {
+  struct timeval tv_buf;
+
+  adns__consistency(ads,0,cc_entex);
+  adns__must_gettimeofday(ads,&now,&tv_buf);
+  if (now) {
+    adns__timeouts(ads, 1, 0,0, *now);
+    adns__fdevents(ads, fds,nfds, 0,0,0,0, *now,0);
+  }
+  adns__consistency(ads,0,cc_entex);
+}
+
+int adns_wait_poll(adns_state ads,
+                  adns_query *query_io,
+                  adns_answer **answer_r,
+                  void **context_r) {
+  int r, nfds, to;
+  struct pollfd fds[MAX_POLLFDS];
+  
+  adns__consistency(ads,0,cc_entex);
+
+  for (;;) {
+    r= adns__internal_check(ads,query_io,answer_r,context_r);
+    if (r != EAGAIN) goto xit;
+    nfds= MAX_POLLFDS; to= -1;
+    adns_beforepoll(ads,fds,&nfds,&to,0);
+    r= poll(fds,nfds,to);
+    if (r == -1) {
+      if (errno == EINTR) {
+       if (ads->iflags & adns_if_eintr) { r= EINTR; goto xit; }
+      } else {
+       adns__diag(ads,-1,0,"poll failed in wait: %s",strerror(errno));
+       adns_globalsystemfailure(ads);
+      }
+    } else {
+      assert(r >= 0);
+      adns_afterpoll(ads,fds,nfds,0);
+    }
+  }
+
+ xit:
+  adns__consistency(ads,0,cc_entex);
+  return r;
+}
+
+#endif
diff --git a/adns/src/query.c b/adns/src/query.c
new file mode 100644 (file)
index 0000000..df8f71e
--- /dev/null
@@ -0,0 +1,590 @@
+/*
+ * query.c
+ * - overall query management (allocation, completion)
+ * - per-query memory management
+ * - query submission and cancellation (user-visible and internal)
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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 "internal.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/time.h>
+
+#include "internal.h"
+
+static adns_query query_alloc(adns_state ads, const typeinfo *typei,
+                             adns_queryflags flags, struct timeval now) {
+  /* Allocate a virgin query and return it. */
+  adns_query qu;
+  
+  qu= malloc(sizeof(*qu));  if (!qu) return 0;
+  qu->answer= malloc(sizeof(*qu->answer));  if (!qu->answer) { free(qu); return 0; }
+  
+  qu->ads= ads;
+  qu->state= query_tosend;
+  qu->back= qu->next= qu->parent= 0;
+  LIST_INIT(qu->children);
+  LINK_INIT(qu->siblings);
+  LIST_INIT(qu->allocations);
+  qu->interim_allocd= 0;
+  qu->preserved_allocd= 0;
+  qu->final_allocspace= 0;
+
+  qu->typei= typei;
+  qu->query_dgram= 0;
+  qu->query_dglen= 0;
+  adns__vbuf_init(&qu->vb);
+
+  qu->cname_dgram= 0;
+  qu->cname_dglen= qu->cname_begin= 0;
+
+  adns__vbuf_init(&qu->search_vb);
+  qu->search_origlen= qu->search_pos= qu->search_doneabs= 0;
+
+  qu->id= -2; /* will be overwritten with real id before we leave adns */
+  qu->flags= flags;
+  qu->retries= 0;
+  qu->udpnextserver= 0;
+  qu->udpsent= 0;
+  timerclear(&qu->timeout);
+  qu->expires= now.tv_sec + MAXTTLBELIEVE;
+
+  memset(&qu->ctx,0,sizeof(qu->ctx));
+
+  qu->answer->status= adns_s_ok;
+  qu->answer->cname= qu->answer->owner= 0;
+  qu->answer->type= typei->type;
+  qu->answer->expires= -1;
+  qu->answer->nrrs= 0;
+  qu->answer->rrs.untyped= 0;
+  qu->answer->rrsz= typei->rrsz;
+
+  return qu;
+}
+
+static void query_submit(adns_state ads, adns_query qu,
+                        const typeinfo *typei, vbuf *qumsg_vb, int id,
+                        adns_queryflags flags, struct timeval now) {
+  /* Fills in the query message in for a previously-allocated query,
+   * and submits it.  Cannot fail.  Takes over the memory for qumsg_vb.
+   */
+
+  qu->vb= *qumsg_vb;
+  adns__vbuf_init(qumsg_vb);
+
+  qu->query_dgram= malloc(qu->vb.used);
+  if (!qu->query_dgram) { adns__query_fail(qu,adns_s_nomemory); return; }
+  
+  qu->id= id;
+  qu->query_dglen= qu->vb.used;
+  memcpy(qu->query_dgram,qu->vb.buf,qu->vb.used);
+  
+  adns__query_send(qu,now);
+}
+
+adns_status adns__internal_submit(adns_state ads, adns_query *query_r,
+                                 const typeinfo *typei, vbuf *qumsg_vb, int id,
+                                 adns_queryflags flags, struct timeval now,
+                                 const qcontext *ctx) {
+  adns_query qu;
+
+  qu= query_alloc(ads,typei,flags,now);
+  if (!qu) { adns__vbuf_free(qumsg_vb); return adns_s_nomemory; }
+  *query_r= qu;
+
+  memcpy(&qu->ctx,ctx,sizeof(qu->ctx));
+  query_submit(ads,qu, typei,qumsg_vb,id,flags,now);
+  
+  return adns_s_ok;
+}
+
+static void query_simple(adns_state ads, adns_query qu,
+                        const char *owner, int ol,
+                        const typeinfo *typei, adns_queryflags flags,
+                        struct timeval now) {
+  vbuf vb_new;
+  int id;
+  adns_status stat;
+
+  stat= adns__mkquery(ads,&qu->vb,&id, owner,ol, typei,flags);
+  if (stat) {
+    if (stat == adns_s_querydomaintoolong && (flags & adns_qf_search)) {
+      adns__search_next(ads,qu,now);
+      return;
+    } else {
+      adns__query_fail(qu,stat);
+      return;
+    }
+  }
+
+  vb_new= qu->vb;
+  adns__vbuf_init(&qu->vb);
+  query_submit(ads,qu, typei,&vb_new,id, flags,now);
+}
+
+void adns__search_next(adns_state ads, adns_query qu, struct timeval now) {
+  const char *nextentry;
+  adns_status stat;
+  
+  if (qu->search_doneabs<0) {
+    nextentry= 0;
+    qu->search_doneabs= 1;
+  } else {
+    if (qu->search_pos >= ads->nsearchlist) {
+      if (qu->search_doneabs) {
+       stat= adns_s_nxdomain; goto x_fail;
+       return;
+      } else {
+       nextentry= 0;
+       qu->search_doneabs= 1;
+      }
+    } else {
+      nextentry= ads->searchlist[qu->search_pos++];
+    }
+  }
+
+  qu->search_vb.used= qu->search_origlen;
+  if (nextentry) {
+    if (!adns__vbuf_append(&qu->search_vb,".",1) ||
+       !adns__vbuf_appendstr(&qu->search_vb,nextentry)) {
+      stat= adns_s_nomemory; goto x_fail;
+    }
+  }
+
+  free(qu->query_dgram);
+  qu->query_dgram= 0; qu->query_dglen= 0;
+
+  query_simple(ads,qu, qu->search_vb.buf, qu->search_vb.used, qu->typei, qu->flags, now);
+  return;
+  
+x_fail:
+  adns__query_fail(qu,stat);
+}
+
+static int save_owner(adns_query qu, const char *owner, int ol) {
+  /* Returns 1 if OK, otherwise there was no memory. */
+  adns_answer *ans;
+
+  ans= qu->answer;
+  assert(!ans->owner);
+
+  ans->owner= adns__alloc_preserved(qu,ol+1);  if (!ans->owner) return 0;
+
+  memcpy(ans->owner,owner,ol);
+  ans->owner[ol]= 0;
+  return 1;
+}
+
+int adns_submit(adns_state ads,
+               const char *owner,
+               adns_rrtype type,
+               adns_queryflags flags,
+               void *context,
+               adns_query *query_r) {
+  int r, ol, ndots;
+  adns_status stat;
+  const typeinfo *typei;
+  struct timeval now;
+  adns_query qu;
+  const char *p;
+
+  adns__consistency(ads,0,cc_entex);
+
+  typei= adns__findtype(type);
+  if (!typei) return ENOSYS;
+
+  r= gettimeofday(&now,0); if (r) goto x_errno;
+  qu= query_alloc(ads,typei,flags,now); if (!qu) goto x_errno;
+  
+  qu->ctx.ext= context;
+  qu->ctx.callback= 0;
+  memset(&qu->ctx.info,0,sizeof(qu->ctx.info));
+
+  *query_r= qu;
+
+  ol= strlen(owner);
+  if (!ol) { stat= adns_s_querydomaininvalid; goto x_adnsfail; }
+  if (ol>DNS_MAXDOMAIN+1) { stat= adns_s_querydomaintoolong; goto x_adnsfail; }
+                                
+  if (ol>=1 && owner[ol-1]=='.' && (ol<2 || owner[ol-2]!='\\')) {
+    flags &= ~adns_qf_search;
+    qu->flags= flags;
+    ol--;
+  }
+
+  if (flags & adns_qf_search) {
+    r= adns__vbuf_append(&qu->search_vb,owner,ol);
+    if (!r) { stat= adns_s_nomemory; goto x_adnsfail; }
+
+    for (ndots=0, p=owner; (p= strchr(p,'.')); p++, ndots++);
+    qu->search_doneabs= (ndots >= ads->searchndots) ? -1 : 0;
+    qu->search_origlen= ol;
+    adns__search_next(ads,qu,now);
+  } else {
+    if (flags & adns_qf_owner) {
+      if (!save_owner(qu,owner,ol)) { stat= adns_s_nomemory; goto x_adnsfail; }
+    }
+    query_simple(ads,qu, owner,ol, typei,flags, now);
+  }
+  adns__autosys(ads,now);
+  adns__consistency(ads,qu,cc_entex);
+  return 0;
+
+ x_adnsfail:
+  adns__query_fail(qu,stat);
+  adns__consistency(ads,qu,cc_entex);
+  return 0;
+
+ x_errno:
+  r= errno;
+  assert(r);
+  adns__consistency(ads,0,cc_entex);
+  return r;
+}
+
+int adns_submit_callback(adns_state ads,
+               const char *owner,
+               adns_rrtype type,
+               adns_queryflags flags,
+               void *context,
+               adns_query *query_r,
+               void (*callback) (adns_state, adns_query, void *))
+{
+  int res = adns_submit(ads, owner, type, flags, context, query_r);
+  if (!res) {
+    assert(query_r);
+    (*query_r)->callback = callback;
+  }
+  return res;
+}
+
+int adns_submit_reverse_any(adns_state ads,
+                           const struct sockaddr *addr,
+                           const char *zone,
+                           adns_rrtype type,
+                           adns_queryflags flags,
+                           void *context,
+                           adns_query *query_r) {
+  const unsigned char *iaddr;
+  char *buf, *buf_free;
+  char shortbuf[100];
+  int r, lreq;
+
+  flags &= ~adns_qf_search;
+
+  if (addr->sa_family != AF_INET) return ENOSYS;
+  iaddr= (const unsigned char*) &(((const struct sockaddr_in*)addr) -> sin_addr);
+
+  lreq= strlen(zone) + 4*4 + 1;
+  if (lreq > sizeof(shortbuf)) {
+    buf= malloc(strlen(zone) + 4*4 + 1);
+    if (!buf) return errno;
+    buf_free= buf;
+  } else {
+    buf= shortbuf;
+    buf_free= 0;
+  }
+  sprintf(buf, "%d.%d.%d.%d.%s", iaddr[3], iaddr[2], iaddr[1], iaddr[0], zone);
+
+  r= adns_submit(ads,buf,type,flags,context,query_r);
+  free(buf_free);
+  return r;
+}
+
+int adns_submit_reverse(adns_state ads,
+                       const struct sockaddr *addr,
+                       adns_rrtype type,
+                       adns_queryflags flags,
+                       void *context,
+                       adns_query *query_r) {
+  if (type != adns_r_ptr && type != adns_r_ptr_raw) return EINVAL;
+  return adns_submit_reverse_any(ads,addr,"in-addr.arpa",type,flags,context,query_r);
+}
+
+int adns_synchronous(adns_state ads,
+                    const char *owner,
+                    adns_rrtype type,
+                    adns_queryflags flags,
+                    adns_answer **answer_r) {
+  adns_query qu;
+  int r;
+  
+  r= adns_submit(ads,owner,type,flags,0,&qu);
+  if (r) return r;
+
+  r= adns_wait(ads,&qu,answer_r,0);
+  if (r) adns_cancel(qu);
+
+  return r;
+}
+
+static void *alloc_common(adns_query qu, size_t sz) {
+  allocnode *an;
+
+  if (!sz) return qu; /* Any old pointer will do */
+  assert(!qu->final_allocspace);
+  an= malloc(MEM_ROUND(MEM_ROUND(sizeof(*an)) + sz));
+  if (!an) return 0;
+  LIST_LINK_TAIL(qu->allocations,an);
+  return (byte*)an + MEM_ROUND(sizeof(*an));
+}
+
+void *adns__alloc_interim(adns_query qu, size_t sz) {
+  void *rv;
+  
+  sz= MEM_ROUND(sz);
+  rv= alloc_common(qu,sz);
+  if (!rv) return 0;
+  qu->interim_allocd += sz;
+  return rv;
+}
+
+void *adns__alloc_preserved(adns_query qu, size_t sz) {
+  void *rv;
+  
+  sz= MEM_ROUND(sz);
+  rv= adns__alloc_interim(qu,sz);
+  if (!rv) return 0;
+  qu->preserved_allocd += sz;
+  return rv;
+}
+
+void *adns__alloc_mine(adns_query qu, size_t sz) {
+  return alloc_common(qu,MEM_ROUND(sz));
+}
+
+void adns__transfer_interim(adns_query from, adns_query to, void *block, size_t sz) {
+  allocnode *an;
+
+  if (!block) return;
+  an= (void*)((byte*)block - MEM_ROUND(sizeof(*an)));
+
+  assert(!to->final_allocspace);
+  assert(!from->final_allocspace);
+  
+  LIST_UNLINK(from->allocations,an);
+  LIST_LINK_TAIL(to->allocations,an);
+
+  sz= MEM_ROUND(sz);
+  from->interim_allocd -= sz;
+  to->interim_allocd += sz;
+
+  if (to->expires > from->expires) to->expires= from->expires;
+}
+
+void *adns__alloc_final(adns_query qu, size_t sz) {
+  /* When we're in the _final stage, we _subtract_ from interim_alloc'd
+   * each allocation, and use final_allocspace to point to the next free
+   * bit.
+   */
+  void *rp;
+
+  sz= MEM_ROUND(sz);
+  rp= qu->final_allocspace;
+  assert(rp);
+  qu->interim_allocd -= sz;
+  assert(qu->interim_allocd>=0);
+  qu->final_allocspace= (byte*)rp + sz;
+  return rp;
+}
+
+static void cancel_children(adns_query qu) {
+  adns_query cqu, ncqu;
+
+  for (cqu= qu->children.head; cqu; cqu= ncqu) {
+    ncqu= cqu->siblings.next;
+    adns_cancel(cqu);
+  }
+}
+
+void adns__reset_preserved(adns_query qu) {
+  assert(!qu->final_allocspace);
+  cancel_children(qu);
+  qu->answer->nrrs= 0;
+  qu->answer->rrs.untyped= 0;
+  qu->interim_allocd= qu->preserved_allocd;
+}
+
+static void free_query_allocs(adns_query qu) {
+  allocnode *an, *ann;
+
+  cancel_children(qu);
+  for (an= qu->allocations.head; an; an= ann) { ann= an->next; free(an); }
+  LIST_INIT(qu->allocations);
+  adns__vbuf_free(&qu->vb);
+  adns__vbuf_free(&qu->search_vb);
+  free(qu->query_dgram);
+  qu->query_dgram= 0;
+}
+
+void adns_cancel(adns_query qu) {
+  adns_state ads;
+
+  ads= qu->ads;
+  adns__consistency(ads,qu,cc_entex);
+  if (qu->parent) LIST_UNLINK_PART(qu->parent->children,qu,siblings.);
+  switch (qu->state) {
+  case query_tosend:
+    LIST_UNLINK(ads->udpw,qu);
+    break;
+  case query_tcpw:
+    LIST_UNLINK(ads->tcpw,qu);
+    break;
+  case query_childw:
+    LIST_UNLINK(ads->childw,qu);
+    break;
+  case query_done:
+    LIST_UNLINK(ads->output,qu);
+    break;
+  default:
+    abort();
+  }
+  free_query_allocs(qu);
+  free(qu->answer);
+  free(qu);
+  adns__consistency(ads,0,cc_entex);
+}
+
+void adns__update_expires(adns_query qu, unsigned long ttl, struct timeval now) {
+  time_t max;
+
+  assert(ttl <= MAXTTLBELIEVE);
+  max= now.tv_sec + ttl;
+  if (qu->expires < max) return;
+  qu->expires= max;
+}
+
+static void makefinal_query(adns_query qu) {
+  adns_answer *ans;
+  int rrn;
+
+  ans= qu->answer;
+
+  if (qu->interim_allocd) {
+    ans= realloc(qu->answer, MEM_ROUND(MEM_ROUND(sizeof(*ans)) + qu->interim_allocd));
+    if (!ans) goto x_nomem;
+    qu->answer= ans;
+  }
+
+  qu->final_allocspace= (byte*)ans + MEM_ROUND(sizeof(*ans));
+  adns__makefinal_str(qu,&ans->cname);
+  adns__makefinal_str(qu,&ans->owner);
+  
+  if (ans->nrrs) {
+    adns__makefinal_block(qu, &ans->rrs.untyped, ans->nrrs*ans->rrsz);
+
+    for (rrn=0; rrn<ans->nrrs; rrn++)
+      qu->typei->makefinal(qu, ans->rrs.bytes + rrn*ans->rrsz);
+  }
+  
+  free_query_allocs(qu);
+  return;
+  
+ x_nomem:
+  qu->preserved_allocd= 0;
+  qu->answer->cname= 0;
+  qu->answer->owner= 0;
+  adns__reset_preserved(qu); /* (but we just threw away the preserved stuff) */
+
+  qu->answer->status= adns_s_nomemory;
+  free_query_allocs(qu);
+}
+
+void adns__query_done(adns_query qu) {
+  adns_answer *ans;
+  adns_query parent;
+
+  cancel_children(qu);
+
+  qu->id= -1;
+  ans= qu->answer;
+
+  if (qu->flags & adns_qf_owner && qu->flags & adns_qf_search &&
+      ans->status != adns_s_nomemory) {
+    if (!save_owner(qu, qu->search_vb.buf, qu->search_vb.used)) {
+      adns__query_fail(qu,adns_s_nomemory);
+      return;
+    }
+  }
+
+  if (ans->nrrs && qu->typei->diff_needswap) {
+    if (!adns__vbuf_ensure(&qu->vb,qu->typei->rrsz)) {
+      adns__query_fail(qu,adns_s_nomemory);
+      return;
+    }
+    adns__isort(ans->rrs.bytes, ans->nrrs, ans->rrsz,
+               qu->vb.buf,
+               (int(*)(void*, const void*, const void*))qu->typei->diff_needswap,
+               qu->ads);
+  }
+
+  ans->expires= qu->expires;
+  parent= qu->parent;
+  if (parent) {
+    LIST_UNLINK_PART(parent->children,qu,siblings.);
+    LIST_UNLINK(qu->ads->childw,parent);
+    qu->ctx.callback(parent,qu);
+    free_query_allocs(qu);
+    free(qu->answer);
+    free(qu);
+  } else {
+    makefinal_query(qu);
+    LIST_LINK_TAIL(qu->ads->output,qu);
+    qu->state= query_done;
+    if (qu->callback)
+      qu->callback(qu->ads, qu, qu->ctx.ext);
+  }
+}
+
+void adns__query_fail(adns_query qu, adns_status stat) {
+  adns__reset_preserved(qu);
+  qu->answer->status= stat;
+  adns__query_done(qu);
+}
+
+void adns__makefinal_str(adns_query qu, char **strp) {
+  int l;
+  char *before, *after;
+
+  before= *strp;
+  if (!before) return;
+  l= strlen(before)+1;
+  after= adns__alloc_final(qu,l);
+  memcpy(after,before,l);
+  *strp= after;  
+}
+
+void adns__makefinal_block(adns_query qu, void **blpp, size_t sz) {
+  void *before, *after;
+
+  before= *blpp;
+  if (!before) return;
+  after= adns__alloc_final(qu,sz);
+  memcpy(after,before,sz);
+  *blpp= after;
+}
diff --git a/adns/src/reply.c b/adns/src/reply.c
new file mode 100644 (file)
index 0000000..83988a0
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * reply.c
+ * - main handling and parsing routine for received datagrams
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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 <stdlib.h>
+
+#include "internal.h"
+    
+void adns__procdgram(adns_state ads, const byte *dgram, int dglen,
+                    int serv, int viatcp, struct timeval now) {
+  int cbyte, rrstart, wantedrrs, rri, foundsoa, foundns, cname_here;
+  int id, f1, f2, qdcount, ancount, nscount, arcount;
+  int flg_ra, flg_rd, flg_tc, flg_qr, opcode;
+  int rrtype, rrclass, rdlength, rdstart;
+  int anstart, nsstart, arstart;
+  int ownermatched, l, nrrs;
+  unsigned long ttl, soattl;
+  const typeinfo *typei;
+  adns_query qu, nqu;
+  dns_rcode rcode;
+  adns_status st;
+  vbuf tempvb;
+  byte *newquery, *rrsdata;
+  parseinfo pai;
+  
+  if (dglen<DNS_HDRSIZE) {
+    adns__diag(ads,serv,0,"received datagram too short for message header (%d)",dglen);
+    return;
+  }
+  cbyte= 0;
+  GET_W(cbyte,id);
+  GET_B(cbyte,f1);
+  GET_B(cbyte,f2);
+  GET_W(cbyte,qdcount);
+  GET_W(cbyte,ancount);
+  GET_W(cbyte,nscount);
+  GET_W(cbyte,arcount);
+  assert(cbyte == DNS_HDRSIZE);
+
+  flg_qr= f1&0x80;
+  opcode= (f1&0x78)>>3;
+  flg_tc= f1&0x02;
+  flg_rd= f1&0x01;
+  flg_ra= f2&0x80;
+  rcode= (f2&0x0f);
+
+  cname_here= 0;
+  
+  if (!flg_qr) {
+    adns__diag(ads,serv,0,"server sent us a query, not a response");
+    return;
+  }
+  if (opcode) {
+    adns__diag(ads,serv,0,"server sent us unknown opcode %d (wanted 0=QUERY)",opcode);
+    return;
+  }
+
+  qu= 0;
+  /* See if we can find the relevant query, or leave qu=0 otherwise ... */   
+
+  if (qdcount == 1) {
+    for (qu= viatcp ? ads->tcpw.head : ads->udpw.head; qu; qu= nqu) {
+      nqu= qu->next;
+      if (qu->id != id) continue;
+      if (dglen < qu->query_dglen) continue;
+      if (memcmp(qu->query_dgram+DNS_HDRSIZE,
+                dgram+DNS_HDRSIZE,
+                qu->query_dglen-DNS_HDRSIZE))
+       continue;
+      if (viatcp) {
+       assert(qu->state == query_tcpw);
+      } else {
+       assert(qu->state == query_tosend);
+       if (!(qu->udpsent & (1<<serv))) continue;
+      }
+      break;
+    }
+    if (qu) {
+      /* We're definitely going to do something with this query now */
+      if (viatcp) LIST_UNLINK(ads->tcpw,qu);
+      else LIST_UNLINK(ads->udpw,qu);
+    }
+  }
+  
+  /* If we're going to ignore the packet, we return as soon as we have
+   * failed the query (if any) and printed the warning message (if
+   * any).
+   */
+  switch (rcode) {
+  case rcode_noerror:
+  case rcode_nxdomain:
+    break;
+  case rcode_formaterror:
+    adns__warn(ads,serv,qu,"server cannot understand our query (Format Error)");
+    if (qu) adns__query_fail(qu,adns_s_rcodeformaterror);
+    return;
+  case rcode_servfail:
+    if (qu) adns__query_fail(qu,adns_s_rcodeservfail);
+    else adns__debug(ads,serv,qu,"server failure on unidentifiable query");
+    return;
+  case rcode_notimp:
+    adns__warn(ads,serv,qu,"server claims not to implement our query");
+    if (qu) adns__query_fail(qu,adns_s_rcodenotimplemented);
+    return;
+  case rcode_refused:
+    adns__debug(ads,serv,qu,"server refused our query");
+    if (qu) adns__query_fail(qu,adns_s_rcoderefused);
+    return;
+  default:
+    adns__warn(ads,serv,qu,"server gave unknown response code %d",rcode);
+    if (qu) adns__query_fail(qu,adns_s_rcodeunknown);
+    return;
+  }
+
+  if (!qu) {
+    if (!qdcount) {
+      adns__diag(ads,serv,0,"server sent reply without quoting our question");
+    } else if (qdcount>1) {
+      adns__diag(ads,serv,0,"server claimed to answer %d questions with one message",
+                qdcount);
+    } else if (ads->iflags & adns_if_debug) {
+      adns__vbuf_init(&tempvb);
+      adns__debug(ads,serv,0,"reply not found, id %02x, query owner %s",
+                 id, adns__diag_domain(ads,serv,0,&tempvb,dgram,dglen,DNS_HDRSIZE));
+      adns__vbuf_free(&tempvb);
+    }
+    return;
+  }
+
+  /* We're definitely going to do something with this packet and this query now. */
+  
+  anstart= qu->query_dglen;
+  arstart= -1;
+
+  /* Now, take a look at the answer section, and see if it is complete.
+   * If it has any CNAMEs we stuff them in the answer.
+   */
+  wantedrrs= 0;
+  cbyte= anstart;
+  for (rri= 0; rri<ancount; rri++) {
+    rrstart= cbyte;
+    st= adns__findrr(qu,serv, dgram,dglen,&cbyte,
+                    &rrtype,&rrclass,&ttl, &rdlength,&rdstart,
+                    &ownermatched);
+    if (st) { adns__query_fail(qu,st); return; }
+    if (rrtype == -1) goto x_truncated;
+
+    if (rrclass != DNS_CLASS_IN) {
+      adns__diag(ads,serv,qu,"ignoring answer RR with wrong class %d (expected IN=%d)",
+                rrclass,DNS_CLASS_IN);
+      continue;
+    }
+    if (!ownermatched) {
+      if (ads->iflags & adns_if_debug) {
+       adns__debug(ads,serv,qu,"ignoring RR with an unexpected owner %s",
+                   adns__diag_domain(ads,serv,qu, &qu->vb, dgram,dglen,rrstart));
+      }
+      continue;
+    }
+    if (rrtype == adns_r_cname &&
+       (qu->typei->type & adns__rrt_typemask) != adns_r_cname) {
+      if (qu->flags & adns_qf_cname_forbid) {
+       adns__query_fail(qu,adns_s_prohibitedcname);
+       return;
+      } else if (qu->cname_dgram) { /* Ignore second and subsequent CNAME(s) */
+       adns__debug(ads,serv,qu,"allegedly canonical name %s is actually alias for %s",
+                   qu->answer->cname,
+                   adns__diag_domain(ads,serv,qu, &qu->vb, dgram,dglen,rdstart));
+       adns__query_fail(qu,adns_s_prohibitedcname);
+       return;
+      } else if (wantedrrs) { /* Ignore CNAME(s) after RR(s). */
+       adns__debug(ads,serv,qu,"ignoring CNAME (to %s) coexisting with RR",
+                   adns__diag_domain(ads,serv,qu, &qu->vb, dgram,dglen,rdstart));
+      } else {
+       qu->cname_begin= rdstart;
+       qu->cname_dglen= dglen;
+       st= adns__parse_domain(ads,serv,qu, &qu->vb,
+                              qu->flags & adns_qf_quotefail_cname ? 0 : pdf_quoteok,
+                              dgram,dglen, &rdstart,rdstart+rdlength);
+       if (!qu->vb.used) goto x_truncated;
+       if (st) { adns__query_fail(qu,st); return; }
+       l= strlen(qu->vb.buf)+1;
+       qu->answer->cname= adns__alloc_preserved(qu,l);
+       if (!qu->answer->cname) { adns__query_fail(qu,adns_s_nomemory); return; }
+
+       qu->cname_dgram= adns__alloc_mine(qu,dglen);
+       memcpy(qu->cname_dgram,dgram,dglen);
+
+       memcpy(qu->answer->cname,qu->vb.buf,l);
+       cname_here= 1;
+       adns__update_expires(qu,ttl,now);
+       /* If we find the answer section truncated after this point we restart
+        * the query at the CNAME; if beforehand then we obviously have to use
+        * TCP.  If there is no truncation we can use the whole answer if
+        * it contains the relevant info.
+        */
+      }
+    } else if (rrtype == (qu->typei->type & adns__rrt_typemask)) {
+      wantedrrs++;
+    } else {
+      adns__debug(ads,serv,qu,"ignoring answer RR with irrelevant type %d",rrtype);
+    }
+  }
+
+  /* We defer handling truncated responses here, in case there was a CNAME
+   * which we could use.
+   */
+  if (flg_tc) goto x_truncated;
+  
+  nsstart= cbyte;
+
+  if (!wantedrrs) {
+    /* Oops, NODATA or NXDOMAIN or perhaps a referral (which would be a problem) */
+
+    /* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records in authority section */
+    foundsoa= 0; soattl= 0; foundns= 0;
+    for (rri= 0; rri<nscount; rri++) {
+      rrstart= cbyte;
+      st= adns__findrr(qu,serv, dgram,dglen,&cbyte,
+                      &rrtype,&rrclass,&ttl, &rdlength,&rdstart, 0);
+      if (st) { adns__query_fail(qu,st); return; }
+      if (rrtype==-1) goto x_truncated;
+      if (rrclass != DNS_CLASS_IN) {
+       adns__diag(ads,serv,qu,
+                  "ignoring authority RR with wrong class %d (expected IN=%d)",
+                  rrclass,DNS_CLASS_IN);
+       continue;
+      }
+      if (rrtype == adns_r_soa_raw) { foundsoa= 1; soattl= ttl; break; }
+      else if (rrtype == adns_r_ns_raw) { foundns= 1; }
+    }
+    
+    if (rcode == rcode_nxdomain) {
+      /* We still wanted to look for the SOA so we could find the TTL. */
+      adns__update_expires(qu,soattl,now);
+
+      if (qu->flags & adns_qf_search) {
+       adns__search_next(ads,qu,now);
+      } else {
+       adns__query_fail(qu,adns_s_nxdomain);
+      }
+      return;
+    }
+
+    if (foundsoa || !foundns) {
+      /* Aha !  A NODATA response, good. */
+      adns__update_expires(qu,soattl,now);
+      adns__query_fail(qu,adns_s_nodata);
+      return;
+    }
+
+    /* Now what ?  No relevant answers, no SOA, and at least some NS's.
+     * Looks like a referral.  Just one last chance ... if we came across
+     * a CNAME in this datagram then we should probably do our own CNAME
+     * lookup now in the hope that we won't get a referral again.
+     */
+    if (cname_here) goto x_restartquery;
+
+    /* Bloody hell, I thought we asked for recursion ? */
+    if (!flg_ra) {
+      adns__diag(ads,serv,qu,"server is not willing to do recursive lookups for us");
+      adns__query_fail(qu,adns_s_norecurse);
+    } else {
+      if (!flg_rd)
+       adns__diag(ads,serv,qu,"server thinks we didn't ask for recursive lookup");
+      else
+       adns__debug(ads,serv,qu,"server claims to do recursion, but gave us a referral");
+      adns__query_fail(qu,adns_s_invalidresponse);
+    }
+    return;
+  }
+
+  /* Now, we have some RRs which we wanted. */
+
+  qu->answer->rrs.untyped= adns__alloc_interim(qu,qu->typei->rrsz*wantedrrs);
+  if (!qu->answer->rrs.untyped) { adns__query_fail(qu,adns_s_nomemory); return; }
+
+  typei= qu->typei;
+  cbyte= anstart;
+  rrsdata= qu->answer->rrs.bytes;
+
+  pai.ads= qu->ads;
+  pai.qu= qu;
+  pai.serv= serv;
+  pai.dgram= dgram;
+  pai.dglen= dglen;
+  pai.nsstart= nsstart;
+  pai.nscount= nscount;
+  pai.arcount= arcount;
+  pai.now= now;
+
+  for (rri=0, nrrs=0; rri<ancount; rri++) {
+    st= adns__findrr(qu,serv, dgram,dglen,&cbyte,
+                    &rrtype,&rrclass,&ttl, &rdlength,&rdstart,
+                    &ownermatched);
+    assert(!st); assert(rrtype != -1);
+    if (rrclass != DNS_CLASS_IN ||
+       rrtype != (qu->typei->type & adns__rrt_typemask) ||
+       !ownermatched)
+      continue;
+    adns__update_expires(qu,ttl,now);
+    st= typei->parse(&pai, rdstart,rdstart+rdlength, rrsdata+nrrs*typei->rrsz);
+    if (st) { adns__query_fail(qu,st); return; }
+    if (rdstart==-1) goto x_truncated;
+    nrrs++;
+  }
+  assert(nrrs==wantedrrs);
+  qu->answer->nrrs= nrrs;
+
+  /* This may have generated some child queries ... */
+  if (qu->children.head) {
+    qu->state= query_childw;
+    LIST_LINK_TAIL(ads->childw,qu);
+    return;
+  }
+  adns__query_done(qu);
+  return;
+
+ x_truncated:
+  
+  if (!flg_tc) {
+    adns__diag(ads,serv,qu,"server sent datagram which points outside itself");
+    adns__query_fail(qu,adns_s_invalidresponse);
+    return;
+  }
+  qu->flags |= adns_qf_usevc;
+  
+ x_restartquery:
+  if (qu->cname_dgram) {
+    st= adns__mkquery_frdgram(qu->ads,&qu->vb,&qu->id,
+                             qu->cname_dgram, qu->cname_dglen, qu->cname_begin,
+                             qu->typei->type, qu->flags);
+    if (st) { adns__query_fail(qu,st); return; }
+    
+    newquery= realloc(qu->query_dgram,qu->vb.used);
+    if (!newquery) { adns__query_fail(qu,adns_s_nomemory); return; }
+    
+    qu->query_dgram= newquery;
+    qu->query_dglen= qu->vb.used;
+    memcpy(newquery,qu->vb.buf,qu->vb.used);
+  }
+  
+  if (qu->state == query_tcpw) qu->state= query_tosend;
+  qu->retries= 0;
+  adns__reset_preserved(qu);
+  adns__query_send(qu,now);
+}
diff --git a/adns/src/setup.c b/adns/src/setup.c
new file mode 100644 (file)
index 0000000..f4be5dd
--- /dev/null
@@ -0,0 +1,643 @@
+/*
+ * setup.c
+ * - configuration file parsing
+ * - management of global state
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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 <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "internal.h"
+
+static void readconfig(adns_state ads, const char *filename, int warnmissing);
+
+static void addserver(adns_state ads, struct in_addr addr) {
+  int i;
+  struct server *ss;
+  
+  for (i=0; i<ads->nservers; i++) {
+    if (ads->servers[i].addr.s_addr == addr.s_addr) {
+      adns__debug(ads,-1,0,"duplicate nameserver %s ignored",inet_ntoa(addr));
+      return;
+    }
+  }
+  
+  if (ads->nservers>=MAXSERVERS) {
+    adns__diag(ads,-1,0,"too many nameservers, ignoring %s",inet_ntoa(addr));
+    return;
+  }
+
+  ss= ads->servers+ads->nservers;
+  ss->addr= addr;
+  ads->nservers++;
+}
+
+static void freesearchlist(adns_state ads) {
+  if (ads->nsearchlist) free(*ads->searchlist);
+  free(ads->searchlist);
+}
+
+static void saveerr(adns_state ads, int en) {
+  if (!ads->configerrno) ads->configerrno= en;
+}
+
+static void configparseerr(adns_state ads, const char *fn, int lno,
+                          const char *fmt, ...) {
+  va_list al;
+
+  saveerr(ads,EINVAL);
+  if (!ads->diagfile || (ads->iflags & adns_if_noerrprint)) return;
+
+  if (lno==-1) fprintf(ads->diagfile,"adns: %s: ",fn);
+  else fprintf(ads->diagfile,"adns: %s:%d: ",fn,lno);
+  va_start(al,fmt);
+  vfprintf(ads->diagfile,fmt,al);
+  va_end(al);
+  fputc('\n',ads->diagfile);
+}
+
+static int nextword(const char **bufp_io, const char **word_r, int *l_r) {
+  const char *p, *q;
+
+  p= *bufp_io;
+  while (ctype_whitespace(*p)) p++;
+  if (!*p) return 0;
+
+  q= p;
+  while (*q && !ctype_whitespace(*q)) q++;
+
+  *l_r= q-p;
+  *word_r= p;
+  *bufp_io= q;
+
+  return 1;
+}
+
+static void ccf_nameserver(adns_state ads, const char *fn, int lno, const char *buf) {
+  struct in_addr ia;
+  
+  if (!inet_aton(buf,&ia)) {
+    configparseerr(ads,fn,lno,"invalid nameserver address `%s'",buf);
+    return;
+  }
+  adns__debug(ads,-1,0,"using nameserver %s",inet_ntoa(ia));
+  addserver(ads,ia);
+}
+
+static void ccf_search(adns_state ads, const char *fn, int lno, const char *buf) {
+  const char *bufp, *word;
+  char *newchars, **newptrs, **pp;
+  int count, tl, l;
+
+  if (!buf) return;
+
+  bufp= buf;
+  count= 0;
+  tl= 0;
+  while (nextword(&bufp,&word,&l)) { count++; tl += l+1; }
+
+  newptrs= malloc(sizeof(char*)*count);  if (!newptrs) { saveerr(ads,errno); return; }
+  newchars= malloc(tl);  if (!newchars) { saveerr(ads,errno); free(newptrs); return; }
+
+  bufp= buf;
+  pp= newptrs;
+  while (nextword(&bufp,&word,&l)) {
+    *pp++= newchars;
+    memcpy(newchars,word,l);
+    newchars += l;
+    *newchars++ = 0;
+  }
+
+  freesearchlist(ads);
+  ads->nsearchlist= count;
+  ads->searchlist= newptrs;
+}
+
+static void ccf_sortlist(adns_state ads, const char *fn, int lno, const char *buf) {
+  const char *word;
+  char tbuf[200], *slash, *ep;
+  struct in_addr base, mask;
+  int l;
+  unsigned long initial, baselocal;
+
+  if (!buf) return;
+  
+  ads->nsortlist= 0;
+  while (nextword(&buf,&word,&l)) {
+    if (ads->nsortlist >= MAXSORTLIST) {
+      adns__diag(ads,-1,0,"too many sortlist entries, ignoring %.*s onwards",l,word);
+      return;
+    }
+
+    if (l >= sizeof(tbuf)) {
+      configparseerr(ads,fn,lno,"sortlist entry `%.*s' too long",l,word);
+      continue;
+    }
+    
+    memcpy(tbuf,word,l); tbuf[l]= 0;
+    slash= strchr(tbuf,'/');
+    if (slash) *slash++= 0;
+    
+    if (!inet_aton(tbuf,&base)) {
+      configparseerr(ads,fn,lno,"invalid address `%s' in sortlist",tbuf);
+      continue;
+    }
+
+    if (slash) {
+      if (strchr(slash,'.')) {
+       if (!inet_aton(slash,&mask)) {
+         configparseerr(ads,fn,lno,"invalid mask `%s' in sortlist",slash);
+         continue;
+       }
+       if (base.s_addr & ~mask.s_addr) {
+         configparseerr(ads,fn,lno,
+                        "mask `%s' in sortlist overlaps address `%s'",slash,tbuf);
+         continue;
+       }
+      } else {
+       initial= strtoul(slash,&ep,10);
+       if (*ep || initial>32) {
+         configparseerr(ads,fn,lno,"mask length `%s' invalid",slash);
+         continue;
+       }
+       mask.s_addr= htonl((0x0ffffffffUL) << (32-initial));
+      }
+    } else {
+      baselocal= ntohl(base.s_addr);
+      if (!baselocal & 0x080000000UL) /* class A */
+       mask.s_addr= htonl(0x0ff000000UL);
+      else if ((baselocal & 0x0c0000000UL) == 0x080000000UL)
+       mask.s_addr= htonl(0x0ffff0000UL); /* class B */
+      else if ((baselocal & 0x0f0000000UL) == 0x0e0000000UL)
+       mask.s_addr= htonl(0x0ff000000UL); /* class C */
+      else {
+       configparseerr(ads,fn,lno,
+                      "network address `%s' in sortlist is not in classed ranges,"
+                      " must specify mask explicitly", tbuf);
+       continue;
+      }
+    }
+
+    ads->sortlist[ads->nsortlist].base= base;
+    ads->sortlist[ads->nsortlist].mask= mask;
+    ads->nsortlist++;
+  }
+}
+
+static void ccf_options(adns_state ads, const char *fn, int lno, const char *buf) {
+  const char *word;
+  char *ep;
+  unsigned long v;
+  int l;
+
+  if (!buf) return;
+
+  while (nextword(&buf,&word,&l)) {
+    if (l==5 && !memcmp(word,"debug",5)) {
+      ads->iflags |= adns_if_debug;
+      continue;
+    }
+    if (l>=6 && !memcmp(word,"ndots:",6)) {
+      v= strtoul(word+6,&ep,10);
+      if (l==6 || ep != word+l || v > INT_MAX) {
+       configparseerr(ads,fn,lno,"option `%.*s' malformed or has bad value",l,word);
+       continue;
+      }
+      ads->searchndots= v;
+      continue;
+    }
+    if (l>=12 && !memcmp(word,"adns_checkc:",12)) {
+      if (!strcmp(word+12,"none")) {
+       ads->iflags &= ~adns_if_checkc_freq;
+       ads->iflags |= adns_if_checkc_entex;
+      } else if (!strcmp(word+12,"entex")) {
+       ads->iflags &= ~adns_if_checkc_freq;
+       ads->iflags |= adns_if_checkc_entex;
+      } else if (!strcmp(word+12,"freq")) {
+       ads->iflags |= adns_if_checkc_freq;
+      } else {
+       configparseerr(ads,fn,lno, "option adns_checkc has bad value `%s' "
+                      "(must be none, entex or freq", word+12);
+      }
+      continue;
+    }
+    adns__diag(ads,-1,0,"%s:%d: unknown option `%.*s'", fn,lno, l,word);
+  }
+}
+
+static void ccf_clearnss(adns_state ads, const char *fn, int lno, const char *buf) {
+  ads->nservers= 0;
+}
+
+static void ccf_include(adns_state ads, const char *fn, int lno, const char *buf) {
+  if (!*buf) {
+    configparseerr(ads,fn,lno,"`include' directive with no filename");
+    return;
+  }
+  readconfig(ads,buf,1);
+}
+
+static const struct configcommandinfo {
+  const char *name;
+  void (*fn)(adns_state ads, const char *fn, int lno, const char *buf);
+} configcommandinfos[]= {
+  { "nameserver",        ccf_nameserver  },
+  { "domain",            ccf_search      },
+  { "search",            ccf_search      },
+  { "sortlist",          ccf_sortlist    },
+  { "options",           ccf_options     },
+  { "clearnameservers",  ccf_clearnss    },
+  { "include",           ccf_include     },
+  {  0                                   }
+};
+
+typedef union {
+  FILE *file;
+  const char *text;
+} getline_ctx;
+
+static int gl_file(adns_state ads, getline_ctx *src_io, const char *filename,
+                  int lno, char *buf, int buflen) {
+  FILE *file= src_io->file;
+  int c, i;
+  char *p;
+
+  p= buf;
+  buflen--;
+  i= 0;
+    
+  for (;;) { /* loop over chars */
+    if (i == buflen) {
+      adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno);
+      goto x_badline;
+    }
+    c= getc(file);
+    if (!c) {
+      adns__diag(ads,-1,0,"%s:%d: line contains nul, ignored",filename,lno);
+      goto x_badline;
+    } else if (c == '\n') {
+      break;
+    } else if (c == EOF) {
+      if (ferror(file)) {
+       saveerr(ads,errno);
+       adns__diag(ads,-1,0,"%s:%d: read error: %s",filename,lno,strerror(errno));
+       return -1;
+      }
+      if (!i) return -1;
+      break;
+    } else {
+      *p++= c;
+      i++;
+    }
+  }
+
+  *p++= 0;
+  return i;
+
+ x_badline:
+  saveerr(ads,EINVAL);
+  while ((c= getc(file)) != EOF && c != '\n');
+  return -2;
+}
+
+static int gl_text(adns_state ads, getline_ctx *src_io, const char *filename,
+                  int lno, char *buf, int buflen) {
+  const char *cp= src_io->text;
+  int l;
+
+  if (!cp || !*cp) return -1;
+
+  if (*cp == ';' || *cp == '\n') cp++;
+  l= strcspn(cp,";\n");
+  src_io->text = cp+l;
+
+  if (l >= buflen) {
+    adns__diag(ads,-1,0,"%s:%d: line too long, ignored",filename,lno);
+    saveerr(ads,EINVAL);
+    return -2;
+  }
+    
+  memcpy(buf,cp,l);
+  buf[l]= 0;
+  return l;
+}
+
+static void readconfiggeneric(adns_state ads, const char *filename,
+                             int (*getline)(adns_state ads, getline_ctx*,
+                                            const char *filename, int lno,
+                                            char *buf, int buflen),
+                             /* Returns >=0 for success, -1 for EOF or error
+                              * (error will have been reported), or -2 for
+                              * bad line was encountered, try again.
+                              */
+                             getline_ctx gl_ctx) {
+  char linebuf[2000], *p, *q;
+  int lno, l, dirl;
+  const struct configcommandinfo *ccip;
+
+  for (lno=1;
+       (l= getline(ads,&gl_ctx, filename,lno, linebuf,sizeof(linebuf))) != -1;
+       lno++) {
+    if (l == -2) continue;
+    while (l>0 && ctype_whitespace(linebuf[l-1])) l--;
+    linebuf[l]= 0;
+    p= linebuf;
+    while (ctype_whitespace(*p)) p++;
+    if (*p == '#' || !*p) continue;
+    q= p;
+    while (*q && !ctype_whitespace(*q)) q++;
+    dirl= q-p;
+    for (ccip=configcommandinfos;
+        ccip->name && !(strlen(ccip->name)==dirl && !memcmp(ccip->name,p,q-p));
+        ccip++);
+    if (!ccip->name) {
+      adns__diag(ads,-1,0,"%s:%d: unknown configuration directive `%.*s'",
+                filename,lno,q-p,p);
+      continue;
+    }
+    while (ctype_whitespace(*q)) q++;
+    ccip->fn(ads,filename,lno,q);
+  }
+}
+
+static const char *instrum_getenv(adns_state ads, const char *envvar) {
+  const char *value;
+
+  value= getenv(envvar);
+  if (!value) adns__debug(ads,-1,0,"environment variable %s not set",envvar);
+  else adns__debug(ads,-1,0,"environment variable %s set to `%s'",envvar,value);
+  return value;
+}
+
+static void readconfig(adns_state ads, const char *filename, int warnmissing) {
+  getline_ctx gl_ctx;
+  
+  gl_ctx.file= fopen(filename,"r");
+  if (!gl_ctx.file) {
+    if (errno == ENOENT) {
+      if (warnmissing)
+       adns__debug(ads,-1,0,"configuration file `%s' does not exist",filename);
+      return;
+    }
+    saveerr(ads,errno);
+    adns__diag(ads,-1,0,"cannot open configuration file `%s': %s",
+              filename,strerror(errno));
+    return;
+  }
+
+  readconfiggeneric(ads,filename,gl_file,gl_ctx);
+  
+  fclose(gl_ctx.file);
+}
+
+static void readconfigtext(adns_state ads, const char *text, const char *showname) {
+  getline_ctx gl_ctx;
+  
+  gl_ctx.text= text;
+  readconfiggeneric(ads,showname,gl_text,gl_ctx);
+}
+  
+static void readconfigenv(adns_state ads, const char *envvar) {
+  const char *filename;
+
+  if (ads->iflags & adns_if_noenv) {
+    adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar);
+    return;
+  }
+  filename= instrum_getenv(ads,envvar);
+  if (filename) readconfig(ads,filename,1);
+}
+
+static void readconfigenvtext(adns_state ads, const char *envvar) {
+  const char *textdata;
+
+  if (ads->iflags & adns_if_noenv) {
+    adns__debug(ads,-1,0,"not checking environment variable `%s'",envvar);
+    return;
+  }
+  textdata= instrum_getenv(ads,envvar);
+  if (textdata) readconfigtext(ads,textdata,envvar);
+}
+
+
+int adns__setnonblock(adns_state ads, int fd) {
+  int r;
+  
+  r= fcntl(fd,F_GETFL,0); if (r<0) return errno;
+  r |= O_NONBLOCK;
+  r= fcntl(fd,F_SETFL,r); if (r<0) return errno;
+  return 0;
+}
+
+static int init_begin(adns_state *ads_r, adns_initflags flags, FILE *diagfile) {
+  adns_state ads;
+  
+  ads= malloc(sizeof(*ads)); if (!ads) return errno;
+
+  ads->iflags= flags;
+  ads->diagfile= diagfile;
+  ads->configerrno= 0;
+  LIST_INIT(ads->udpw);
+  LIST_INIT(ads->tcpw);
+  LIST_INIT(ads->childw);
+  LIST_INIT(ads->output);
+  ads->forallnext= 0;
+  ads->nextid= 0x311f;
+  ads->udpsocket= ads->tcpsocket= -1;
+  adns__vbuf_init(&ads->tcpsend);
+  adns__vbuf_init(&ads->tcprecv);
+  ads->tcprecv_skip= 0;
+  ads->nservers= ads->nsortlist= ads->nsearchlist= ads->tcpserver= 0;
+  ads->searchndots= 1;
+  ads->tcpstate= server_disconnected;
+  timerclear(&ads->tcptimeout);
+  ads->searchlist= 0;
+
+  *ads_r= ads;
+  return 0;
+}
+
+static int init_finish(adns_state ads) {
+  struct in_addr ia;
+  struct protoent *proto;
+  int r;
+  
+  if (!ads->nservers) {
+    if (ads->diagfile && ads->iflags & adns_if_debug)
+      fprintf(ads->diagfile,"adns: no nameservers, using localhost\n");
+    ia.s_addr= htonl(INADDR_LOOPBACK);
+    addserver(ads,ia);
+  }
+
+  proto= getprotobyname("udp"); if (!proto) { r= ENOPROTOOPT; goto x_free; }
+  ads->udpsocket= socket(AF_INET,SOCK_DGRAM,proto->p_proto);
+  if (ads->udpsocket<0) { r= errno; goto x_free; }
+
+  r= adns__setnonblock(ads,ads->udpsocket);
+  if (r) { r= errno; goto x_closeudp; }
+  
+  return 0;
+
+ x_closeudp:
+  close(ads->udpsocket);
+ x_free:
+  free(ads);
+  return r;
+}
+
+static void init_abort(adns_state ads) {
+  if (ads->nsearchlist) {
+    free(ads->searchlist[0]);
+    free(ads->searchlist);
+  }
+  free(ads);
+}
+
+int adns_init(adns_state *ads_r, adns_initflags flags, FILE *diagfile) {
+  adns_state ads;
+  const char *res_options, *adns_res_options;
+  int r;
+  
+  r= init_begin(&ads, flags, diagfile ? diagfile : stderr);
+  if (r) return r;
+  
+  res_options= instrum_getenv(ads,"RES_OPTIONS");
+  adns_res_options= instrum_getenv(ads,"ADNS_RES_OPTIONS");
+  ccf_options(ads,"RES_OPTIONS",-1,res_options);
+  ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
+
+  readconfig(ads,"/etc/resolv.conf",1);
+  readconfig(ads,"/etc/resolv-adns.conf",0);
+  readconfigenv(ads,"RES_CONF");
+  readconfigenv(ads,"ADNS_RES_CONF");
+
+  readconfigenvtext(ads,"RES_CONF_TEXT");
+  readconfigenvtext(ads,"ADNS_RES_CONF_TEXT");
+
+  ccf_options(ads,"RES_OPTIONS",-1,res_options);
+  ccf_options(ads,"ADNS_RES_OPTIONS",-1,adns_res_options);
+
+  ccf_search(ads,"LOCALDOMAIN",-1,instrum_getenv(ads,"LOCALDOMAIN"));
+  ccf_search(ads,"ADNS_LOCALDOMAIN",-1,instrum_getenv(ads,"ADNS_LOCALDOMAIN"));
+
+  if (ads->configerrno && ads->configerrno != EINVAL) {
+    r= ads->configerrno;
+    init_abort(ads);
+    return r;
+  }
+
+  r= init_finish(ads);
+  if (r) return r;
+
+  adns__consistency(ads,0,cc_entex);
+  *ads_r= ads;
+  return 0;
+}
+
+int adns_init_strcfg(adns_state *ads_r, adns_initflags flags,
+                    FILE *diagfile, const char *configtext) {
+  adns_state ads;
+  int r;
+
+  r= init_begin(&ads, flags, diagfile);  if (r) return r;
+
+  readconfigtext(ads,configtext,"<supplied configuration text>");
+  if (ads->configerrno) {
+    r= ads->configerrno;
+    init_abort(ads);
+    return r;
+  }
+
+  r= init_finish(ads);  if (r) return r;
+  adns__consistency(ads,0,cc_entex);
+  *ads_r= ads;
+  return 0;
+}
+
+
+void adns_finish(adns_state ads) {
+  adns__consistency(ads,0,cc_entex);
+  for (;;) {
+    if (ads->udpw.head) adns_cancel(ads->udpw.head);
+    else if (ads->tcpw.head) adns_cancel(ads->tcpw.head);
+    else if (ads->childw.head) adns_cancel(ads->childw.head);
+    else if (ads->output.head) adns_cancel(ads->output.head);
+    else break;
+  }
+  close(ads->udpsocket);
+  if (ads->tcpsocket >= 0) close(ads->tcpsocket);
+  adns__vbuf_free(&ads->tcpsend);
+  adns__vbuf_free(&ads->tcprecv);
+  freesearchlist(ads);
+  free(ads);
+}
+
+void adns_forallqueries_begin(adns_state ads) {
+  adns__consistency(ads,0,cc_entex);
+  ads->forallnext=
+    ads->udpw.head ? ads->udpw.head :
+    ads->tcpw.head ? ads->tcpw.head :
+    ads->childw.head ? ads->childw.head :
+    ads->output.head;
+}
+  
+adns_query adns_forallqueries_next(adns_state ads, void **context_r) {
+  adns_query qu, nqu;
+
+  adns__consistency(ads,0,cc_entex);
+  nqu= ads->forallnext;
+  for (;;) {
+    qu= nqu;
+    if (!qu) return 0;
+    if (qu->next) {
+      nqu= qu->next;
+    } else if (qu == ads->udpw.tail) {
+      nqu=
+       ads->tcpw.head ? ads->tcpw.head :
+       ads->childw.head ? ads->childw.head :
+       ads->output.head;
+    } else if (qu == ads->tcpw.tail) {
+      nqu=
+       ads->childw.head ? ads->childw.head :
+       ads->output.head;
+    } else if (qu == ads->childw.tail) {
+      nqu= ads->output.head;
+    } else {
+      nqu= 0;
+    }
+    if (!qu->parent) break;
+  }
+  ads->forallnext= nqu;
+  if (context_r) *context_r= qu->ctx.ext;
+  return qu;
+}
diff --git a/adns/src/transmit.c b/adns/src/transmit.c
new file mode 100644 (file)
index 0000000..47785e6
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * transmit.c
+ * - construct queries
+ * - send queries
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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 <errno.h>
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include "internal.h"
+#include "tvarith.h"
+
+#define MKQUERY_START(vb) (rqp= (vb)->buf+(vb)->used)
+#define MKQUERY_ADDB(b) *rqp++= (b)
+#define MKQUERY_ADDW(w) (MKQUERY_ADDB(((w)>>8)&0x0ff), MKQUERY_ADDB((w)&0x0ff))
+#define MKQUERY_STOP(vb) ((vb)->used= rqp-(vb)->buf)
+
+static adns_status mkquery_header(adns_state ads, vbuf *vb, int *id_r, int qdlen) {
+  int id;
+  byte *rqp;
+  
+  if (!adns__vbuf_ensure(vb,DNS_HDRSIZE+qdlen+4)) return adns_s_nomemory;
+
+  vb->used= 0;
+  MKQUERY_START(vb);
+  
+  *id_r= id= (ads->nextid++) & 0x0ffff;
+  MKQUERY_ADDW(id);
+  MKQUERY_ADDB(0x01); /* QR=Q(0), OPCODE=QUERY(0000), !AA, !TC, RD */
+  MKQUERY_ADDB(0x00); /* !RA, Z=000, RCODE=NOERROR(0000) */
+  MKQUERY_ADDW(1); /* QDCOUNT=1 */
+  MKQUERY_ADDW(0); /* ANCOUNT=0 */
+  MKQUERY_ADDW(0); /* NSCOUNT=0 */
+  MKQUERY_ADDW(0); /* ARCOUNT=0 */
+
+  MKQUERY_STOP(vb);
+  
+  return adns_s_ok;
+}
+
+static adns_status mkquery_footer(vbuf *vb, adns_rrtype type) {
+  byte *rqp;
+
+  MKQUERY_START(vb);
+  MKQUERY_ADDW(type & adns__rrt_typemask); /* QTYPE */
+  MKQUERY_ADDW(DNS_CLASS_IN); /* QCLASS=IN */
+  MKQUERY_STOP(vb);
+  assert(vb->used <= vb->avail);
+  
+  return adns_s_ok;
+}
+
+adns_status adns__mkquery(adns_state ads, vbuf *vb, int *id_r,
+                         const char *owner, int ol,
+                         const typeinfo *typei, adns_queryflags flags) {
+  int ll, c, nbytes;
+  byte label[255], *rqp;
+  const char *p, *pe;
+  adns_status st;
+
+  st= mkquery_header(ads,vb,id_r,ol+2); if (st) return st;
+  
+  MKQUERY_START(vb);
+
+  p= owner; pe= owner+ol;
+  nbytes= 0;
+  while (p!=pe) {
+    ll= 0;
+    while (p!=pe && (c= *p++)!='.') {
+      if (c=='\\') {
+       if (!(flags & adns_qf_quoteok_query)) return adns_s_querydomaininvalid;
+       if (ctype_digit(p[0])) {
+         if (ctype_digit(p[1]) && ctype_digit(p[2])) {
+           c= (*p++ - '0')*100 + (*p++ - '0')*10 + (*p++ - '0');
+           if (c >= 256) return adns_s_querydomaininvalid;
+         } else {
+           return adns_s_querydomaininvalid;
+         }
+       } else if (!(c= *p++)) {
+         return adns_s_querydomaininvalid;
+       }
+      }
+      if (!(flags & adns_qf_quoteok_query)) {
+       if (c == '-') {
+         if (!ll) return adns_s_querydomaininvalid;
+       } else if (!ctype_alpha(c) && !ctype_digit(c)) {
+         return adns_s_querydomaininvalid;
+       }
+      }
+      if (ll == sizeof(label)) return adns_s_querydomaininvalid;
+      label[ll++]= c;
+    }
+    if (!ll) return adns_s_querydomaininvalid;
+    if (ll > DNS_MAXLABEL) return adns_s_querydomaintoolong;
+    nbytes+= ll+1;
+    if (nbytes >= DNS_MAXDOMAIN) return adns_s_querydomaintoolong;
+    MKQUERY_ADDB(ll);
+    memcpy(rqp,label,ll); rqp+= ll;
+  }
+  MKQUERY_ADDB(0);
+
+  MKQUERY_STOP(vb);
+  
+  st= mkquery_footer(vb,typei->type);
+  
+  return adns_s_ok;
+}
+
+adns_status adns__mkquery_frdgram(adns_state ads, vbuf *vb, int *id_r,
+                                 const byte *qd_dgram, int qd_dglen, int qd_begin,
+                                 adns_rrtype type, adns_queryflags flags) {
+  byte *rqp;
+  findlabel_state fls;
+  int lablen, labstart;
+  adns_status st;
+
+  st= mkquery_header(ads,vb,id_r,qd_dglen); if (st) return st;
+
+  MKQUERY_START(vb);
+
+  adns__findlabel_start(&fls,ads,-1,0,qd_dgram,qd_dglen,qd_dglen,qd_begin,0);
+  for (;;) {
+    st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
+    if (!lablen) break;
+    assert(lablen<255);
+    MKQUERY_ADDB(lablen);
+    memcpy(rqp,qd_dgram+labstart,lablen);
+    rqp+= lablen;
+  }
+  MKQUERY_ADDB(0);
+
+  MKQUERY_STOP(vb);
+  
+  st= mkquery_footer(vb,type);
+  
+  return adns_s_ok;
+}
+
+void adns__querysend_tcp(adns_query qu, struct timeval now) {
+  byte length[2];
+  struct iovec iov[2];
+  int wr, r;
+  adns_state ads;
+
+  if (qu->ads->tcpstate != server_ok) return;
+
+  assert(qu->state == query_tcpw);
+
+  length[0]= (qu->query_dglen&0x0ff00U) >>8;
+  length[1]= (qu->query_dglen&0x0ff);
+
+  ads= qu->ads;
+  if (!adns__vbuf_ensure(&ads->tcpsend,ads->tcpsend.used+qu->query_dglen+2)) return;
+
+  qu->retries++;
+
+  /* Reset idle timeout. */
+  ads->tcptimeout.tv_sec= ads->tcptimeout.tv_usec= 0;
+
+  if (ads->tcpsend.used) {
+    wr= 0;
+  } else {
+    iov[0].iov_base= length;
+    iov[0].iov_len= 2;
+    iov[1].iov_base= qu->query_dgram;
+    iov[1].iov_len= qu->query_dglen;
+    adns__sigpipe_protect(qu->ads);
+    wr= writev(qu->ads->tcpsocket,iov,2);
+    adns__sigpipe_unprotect(qu->ads);
+    if (wr < 0) {
+      if (!(errno == EAGAIN || errno == EINTR || errno == ENOSPC ||
+           errno == ENOBUFS || errno == ENOMEM)) {
+       adns__tcp_broken(ads,"write",strerror(errno));
+       return;
+      }
+      wr= 0;
+    }
+  }
+
+  if (wr<2) {
+    r= adns__vbuf_append(&ads->tcpsend,length,2-wr); assert(r);
+    wr= 0;
+  } else {
+    wr-= 2;
+  }
+  if (wr<qu->query_dglen) {
+    r= adns__vbuf_append(&ads->tcpsend,qu->query_dgram+wr,qu->query_dglen-wr); assert(r);
+  }
+}
+
+static void query_usetcp(adns_query qu, struct timeval now) {
+  qu->state= query_tcpw;
+  qu->timeout= now;
+  timevaladd(&qu->timeout,TCPWAITMS);
+  LIST_LINK_TAIL(qu->ads->tcpw,qu);
+  adns__querysend_tcp(qu,now);
+  adns__tcp_tryconnect(qu->ads,now);
+}
+
+void adns__query_send(adns_query qu, struct timeval now) {
+  struct sockaddr_in servaddr;
+  int serv, r;
+  adns_state ads;
+
+  assert(qu->state == query_tosend);
+  if ((qu->flags & adns_qf_usevc) || (qu->query_dglen > DNS_MAXUDP)) {
+    query_usetcp(qu,now);
+    return;
+  }
+
+  if (qu->retries >= UDPMAXRETRIES) {
+    adns__query_fail(qu,adns_s_timeout);
+    return;
+  }
+
+  serv= qu->udpnextserver;
+  memset(&servaddr,0,sizeof(servaddr));
+
+  ads= qu->ads;
+  servaddr.sin_family= AF_INET;
+  servaddr.sin_addr= ads->servers[serv].addr;
+  servaddr.sin_port= htons(DNS_PORT);
+  
+  r= sendto(ads->udpsocket,qu->query_dgram,qu->query_dglen,0,
+           (const struct sockaddr*)&servaddr,sizeof(servaddr));
+  if (r<0 && errno == EMSGSIZE) { qu->retries= 0; query_usetcp(qu,now); return; }
+  if (r<0 && errno != EAGAIN) adns__warn(ads,serv,0,"sendto failed: %s",strerror(errno));
+  
+  qu->timeout= now;
+  timevaladd(&qu->timeout,UDPRETRYMS);
+  qu->udpsent |= (1<<serv);
+  qu->udpnextserver= (serv+1)%ads->nservers;
+  qu->retries++;
+  LIST_LINK_TAIL(ads->udpw,qu);
+}
diff --git a/adns/src/tvarith.h b/adns/src/tvarith.h
new file mode 100644 (file)
index 0000000..a401b4a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * tvarith.h
+ * - static inline functions for doing arithmetic on timevals
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *
+ *  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.
+ */
+
+#ifndef ADNS_TVARITH_H_INCLUDED
+#define ADNS_TVARITH_H_INCLUDED
+
+static inline void timevaladd(struct timeval *tv_io, long ms) {
+  struct timeval tmp;
+  assert(ms>=0);
+  tmp= *tv_io;
+  tmp.tv_usec += (ms%1000)*1000;
+  tmp.tv_sec += ms/1000;
+  if (tmp.tv_usec >= 1000000) { tmp.tv_sec++; tmp.tv_usec -= 1000000; }
+  *tv_io= tmp;
+}
+
+#endif
diff --git a/adns/src/types.c b/adns/src/types.c
new file mode 100644 (file)
index 0000000..b1fdae7
--- /dev/null
@@ -0,0 +1,1034 @@
+/*
+ * types.c
+ * - RR-type-specific code, and the machinery to call it
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999-2000 Tony Finch <dot@dotat.at>
+ *  
+ *  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 <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "internal.h"
+
+#define R_NOMEM           return adns_s_nomemory
+#define CSP_ADDSTR(s)     do { if (!adns__vbuf_appendstr(vb,(s))) R_NOMEM; } while (0)
+
+/*
+ * order of sections:
+ *
+ * _string                    (pap)
+ * _textdata, _qstring        (csp)
+ * _str                       (mf,cs)
+ * _intstr                    (mf,csp,cs)
+ * _manyistr                  (mf,cs)
+ * _txt                       (pa)
+ * _inaddr                    (pa,dip,di)
+ * _addr                      (pa,di,csp,cs)
+ * _domain                    (pap)
+ * _host_raw                  (pa)
+ * _hostaddr                  (pap,pa,dip,di,mfp,mf,csp,cs +pap_findaddrs)
+ * _mx_raw                    (pa,di)
+ * _mx                        (pa,di)
+ * _inthostaddr               (mf,cs)
+ * _ptr                       (pa)
+ * _strpair                   (mf,cs)
+ * _intstrpair                (mf,cs)
+ * _hinfo                     (pa)
+ * _mailbox                   (pap +pap_mailbox822)
+ * _rp                        (pa)
+ * _soa                       (pa,mf,cs)
+ * _flat                      (mf)
+ *
+ * within each section:
+ *    pap_*
+ *    pa_*
+ *    dip_*
+ *    di_*
+ *    mfp_*
+ *    mf_*
+ *    csp_*
+ *    cs_*
+ */
+
+/*
+ * _qstring               (pap,csp)
+ */
+
+static adns_status pap_qstring(const parseinfo *pai, int *cbyte_io, int max,
+                             int *len_r, char **str_r) {
+  /* Neither len_r nor str_r may be null.
+   * End of datagram (overrun) is indicated by returning adns_s_invaliddata;
+   */
+  const byte *dgram= pai->dgram;
+  int l, cbyte;
+  char *str;
+
+  cbyte= *cbyte_io;
+
+  if (cbyte >= max) return adns_s_invaliddata;
+  GET_B(cbyte,l);
+  if (cbyte+l > max) return adns_s_invaliddata;
+  
+  str= adns__alloc_interim(pai->qu, l+1);
+  if (!str) R_NOMEM;
+  
+  str[l]= 0;
+  memcpy(str,dgram+cbyte,l);
+
+  *len_r= l;
+  *str_r= str;
+  *cbyte_io= cbyte+l;
+  
+  return adns_s_ok;
+}
+
+static adns_status csp_qstring(vbuf *vb, const char *dp, int len) {
+  unsigned char ch;
+  char buf[10];
+  int cn;
+
+  CSP_ADDSTR("\"");
+  for (cn=0; cn<len; cn++) {
+    ch= *dp++;
+    if (ch == '\\') {
+      CSP_ADDSTR("\\\\");
+    } else if (ch == '"') {
+      CSP_ADDSTR("\\\"");
+    } else if (ch >= 32 && ch <= 126) {
+      if (!adns__vbuf_append(vb,&ch,1)) R_NOMEM;
+    } else {
+      sprintf(buf,"\\x%02x",ch);
+      CSP_ADDSTR(buf);
+    }
+  }
+  CSP_ADDSTR("\"");
+  
+  return adns_s_ok;
+}
+
+/*
+ * _str  (mf)
+ */
+
+static void mf_str(adns_query qu, void *datap) {
+  char **rrp= datap;
+
+  adns__makefinal_str(qu,rrp);
+}
+
+/*
+ * _intstr  (mf)
+ */
+
+static void mf_intstr(adns_query qu, void *datap) {
+  adns_rr_intstr *rrp= datap;
+
+  adns__makefinal_str(qu,&rrp->str);
+}
+
+/*
+ * _manyistr   (mf)
+ */
+
+static void mf_manyistr(adns_query qu, void *datap) {
+  adns_rr_intstr **rrp= datap;
+  adns_rr_intstr *te, *table;
+  void *tablev;
+  int tc;
+
+  for (tc=0, te= *rrp; te->i >= 0; te++, tc++);
+  tablev= *rrp;
+  adns__makefinal_block(qu,&tablev,sizeof(*te)*(tc+1));
+  *rrp= table= tablev;
+  for (te= *rrp; te->i >= 0; te++)
+    adns__makefinal_str(qu,&te->str);
+}
+
+/*
+ * _txt   (pa,cs)
+ */
+
+static adns_status pa_txt(const parseinfo *pai, int cbyte, int max, void *datap) {
+  adns_rr_intstr **rrp= datap, *table, *te;
+  const byte *dgram= pai->dgram;
+  int ti, tc, l, startbyte;
+  adns_status st;
+
+  startbyte= cbyte;
+  if (cbyte >= max) return adns_s_invaliddata;
+  tc= 0;
+  while (cbyte < max) {
+    GET_B(cbyte,l);
+    cbyte+= l;
+    tc++;
+  }
+  if (cbyte != max || !tc) return adns_s_invaliddata;
+
+  table= adns__alloc_interim(pai->qu,sizeof(*table)*(tc+1));
+  if (!table) R_NOMEM;
+
+  for (cbyte=startbyte, ti=0, te=table; ti<tc; ti++, te++) {
+    st= pap_qstring(pai, &cbyte, max, &te->i, &te->str);
+    if (st) return st;
+  }
+  assert(cbyte == max);
+
+  te->i= -1;
+  te->str= 0;
+  
+  *rrp= table;
+  return adns_s_ok;
+}
+
+static adns_status cs_txt(vbuf *vb, const void *datap) {
+  const adns_rr_intstr *const *rrp= datap;
+  const adns_rr_intstr *current;
+  adns_status st;
+  int spc;
+
+  for (current= *rrp, spc=0;  current->i >= 0;  current++, spc=1) {
+    if (spc) CSP_ADDSTR(" ");
+    st= csp_qstring(vb,current->str,current->i); if (st) return st;
+  }
+  return adns_s_ok;
+}
+
+/*
+ * _hinfo   (cs)
+ */
+
+static adns_status cs_hinfo(vbuf *vb, const void *datap) {
+  const adns_rr_intstrpair *rrp= datap;
+  adns_status st;
+
+  st= csp_qstring(vb,rrp->array[0].str,rrp->array[0].i);  if (st) return st;
+  CSP_ADDSTR(" ");
+  st= csp_qstring(vb,rrp->array[1].str,rrp->array[1].i);  if (st) return st;
+  return adns_s_ok;
+}
+
+/*
+ * _inaddr   (pa,dip,di)
+ */
+
+static adns_status pa_inaddr(const parseinfo *pai, int cbyte, int max, void *datap) {
+  struct in_addr *storeto= datap;
+  
+  if (max-cbyte != 4) return adns_s_invaliddata;
+  memcpy(storeto, pai->dgram + cbyte, 4);
+  return adns_s_ok;
+}
+
+static int search_sortlist(adns_state ads, struct in_addr ad) {
+  const struct sortlist *slp;
+  int i;
+  
+  for (i=0, slp=ads->sortlist;
+       i<ads->nsortlist && !((ad.s_addr & slp->mask.s_addr) == slp->base.s_addr);
+       i++, slp++);
+  return i;
+}
+
+static int dip_inaddr(adns_state ads, struct in_addr a, struct in_addr b) {
+  int ai, bi;
+  
+  if (!ads->nsortlist) return 0;
+
+  ai= search_sortlist(ads,a);
+  bi= search_sortlist(ads,b);
+  return bi<ai;
+}
+
+static int di_inaddr(adns_state ads, const void *datap_a, const void *datap_b) {
+  const struct in_addr *ap= datap_a, *bp= datap_b;
+
+  return dip_inaddr(ads,*ap,*bp);
+}
+
+static adns_status cs_inaddr(vbuf *vb, const void *datap) {
+  const struct in_addr *rrp= datap, rr= *rrp;
+  const char *ia;
+
+  ia= inet_ntoa(rr); assert(ia);
+  CSP_ADDSTR(ia);
+  return adns_s_ok;
+}
+
+/*
+ * _addr   (pa,di,csp,cs)
+ */
+
+static adns_status pa_addr(const parseinfo *pai, int cbyte, int max, void *datap) {
+  adns_rr_addr *storeto= datap;
+  const byte *dgram= pai->dgram;
+
+  if (max-cbyte != 4) return adns_s_invaliddata;
+  storeto->len= sizeof(storeto->addr.inet);
+  memset(&storeto->addr,0,sizeof(storeto->addr.inet));
+  storeto->addr.inet.sin_family= AF_INET;
+  memcpy(&storeto->addr.inet.sin_addr,dgram+cbyte,4);
+  return adns_s_ok;
+}
+
+static int di_addr(adns_state ads, const void *datap_a, const void *datap_b) {
+  const adns_rr_addr *ap= datap_a, *bp= datap_b;
+
+  assert(ap->addr.sa.sa_family == AF_INET);
+  return dip_inaddr(ads, ap->addr.inet.sin_addr, bp->addr.inet.sin_addr);
+}
+
+static int div_addr(void *context, const void *datap_a, const void *datap_b) {
+  const adns_state ads= context;
+
+  return di_addr(ads, datap_a, datap_b);
+}                   
+
+static adns_status csp_addr(vbuf *vb, const adns_rr_addr *rrp) {
+  const char *ia;
+  static char buf[30];
+
+  switch (rrp->addr.inet.sin_family) {
+  case AF_INET:
+    CSP_ADDSTR("INET ");
+    ia= inet_ntoa(rrp->addr.inet.sin_addr); assert(ia);
+    CSP_ADDSTR(ia);
+    break;
+  default:
+    sprintf(buf,"AF=%u",rrp->addr.sa.sa_family);
+    CSP_ADDSTR(buf);
+    break;
+  }
+  return adns_s_ok;
+}
+
+static adns_status cs_addr(vbuf *vb, const void *datap) {
+  const adns_rr_addr *rrp= datap;
+
+  return csp_addr(vb,rrp);
+}
+
+/*
+ * _domain      (pap,csp,cs)
+ * _dom_raw     (pa)
+ */
+
+static adns_status pap_domain(const parseinfo *pai, int *cbyte_io, int max,
+                             char **domain_r, parsedomain_flags flags) {
+  adns_status st;
+  char *dm;
+  
+  st= adns__parse_domain(pai->qu->ads, pai->serv, pai->qu, &pai->qu->vb, flags,
+                        pai->dgram,pai->dglen, cbyte_io, max);
+  if (st) return st;
+  if (!pai->qu->vb.used) return adns_s_invaliddata;
+
+  dm= adns__alloc_interim(pai->qu, pai->qu->vb.used+1);
+  if (!dm) R_NOMEM;
+
+  dm[pai->qu->vb.used]= 0;
+  memcpy(dm,pai->qu->vb.buf,pai->qu->vb.used);
+  
+  *domain_r= dm;
+  return adns_s_ok;
+}
+
+static adns_status csp_domain(vbuf *vb, const char *domain) {
+  CSP_ADDSTR(domain);
+  if (!*domain) CSP_ADDSTR(".");
+  return adns_s_ok;
+}
+
+static adns_status cs_domain(vbuf *vb, const void *datap) {
+  const char *const *domainp= datap;
+  return csp_domain(vb,*domainp);
+}
+
+static adns_status pa_dom_raw(const parseinfo *pai, int cbyte, int max, void *datap) {
+  char **rrp= datap;
+  adns_status st;
+
+  st= pap_domain(pai, &cbyte, max, rrp, pdf_quoteok);
+  if (st) return st;
+  
+  if (cbyte != max) return adns_s_invaliddata;
+  return adns_s_ok;
+}
+
+/*
+ * _host_raw   (pa)
+ */
+
+static adns_status pa_host_raw(const parseinfo *pai, int cbyte, int max, void *datap) {
+  char **rrp= datap;
+  adns_status st;
+
+  st= pap_domain(pai, &cbyte, max, rrp,
+                pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0);
+  if (st) return st;
+  
+  if (cbyte != max) return adns_s_invaliddata;
+  return adns_s_ok;
+}
+
+/*
+ * _hostaddr   (pap,pa,dip,di,mfp,mf,csp,cs +icb_hostaddr, pap_findaddrs)
+ */
+
+static adns_status pap_findaddrs(const parseinfo *pai, adns_rr_hostaddr *ha,
+                                int *cbyte_io, int count, int dmstart) {
+  int rri, naddrs;
+  int type, class, rdlen, rdstart, ownermatched;
+  unsigned long ttl;
+  adns_status st;
+  
+  for (rri=0, naddrs=-1; rri<count; rri++) {
+    st= adns__findrr_anychk(pai->qu, pai->serv, pai->dgram, pai->dglen, cbyte_io,
+                           &type, &class, &ttl, &rdlen, &rdstart,
+                           pai->dgram, pai->dglen, dmstart, &ownermatched);
+    if (st) return st;
+    if (!ownermatched || class != DNS_CLASS_IN || type != adns_r_a) {
+      if (naddrs>0) break; else continue;
+    }
+    if (naddrs == -1) {
+      naddrs= 0;
+    }
+    if (!adns__vbuf_ensure(&pai->qu->vb, (naddrs+1)*sizeof(adns_rr_addr))) R_NOMEM;
+    adns__update_expires(pai->qu,ttl,pai->now);
+    st= pa_addr(pai, rdstart,rdstart+rdlen,
+               pai->qu->vb.buf + naddrs*sizeof(adns_rr_addr));
+    if (st) return st;
+    naddrs++;
+  }
+  if (naddrs >= 0) {
+    ha->addrs= adns__alloc_interim(pai->qu, naddrs*sizeof(adns_rr_addr));
+    if (!ha->addrs) R_NOMEM;
+    memcpy(ha->addrs, pai->qu->vb.buf, naddrs*sizeof(adns_rr_addr));
+    ha->naddrs= naddrs;
+    ha->astatus= adns_s_ok;
+
+    adns__isort(ha->addrs, naddrs, sizeof(adns_rr_addr), pai->qu->vb.buf,
+               div_addr, pai->ads);
+  }
+  return adns_s_ok;
+}
+
+static void icb_hostaddr(adns_query parent, adns_query child) {
+  adns_answer *cans= child->answer;
+  adns_rr_hostaddr *rrp= child->ctx.info.hostaddr;
+  adns_state ads= parent->ads;
+  adns_status st;
+
+  st= cans->status;
+  rrp->astatus= st;
+  rrp->naddrs= (st>0 && st<=adns_s_max_tempfail) ? -1 : cans->nrrs;
+  rrp->addrs= cans->rrs.addr;
+  adns__transfer_interim(child, parent, rrp->addrs, rrp->naddrs*sizeof(adns_rr_addr));
+
+  if (parent->children.head) {
+    LIST_LINK_TAIL(ads->childw,parent);
+  } else {
+    adns__query_done(parent);
+  }
+}
+
+static adns_status pap_hostaddr(const parseinfo *pai, int *cbyte_io,
+                               int max, adns_rr_hostaddr *rrp) {
+  adns_status st;
+  int dmstart, cbyte;
+  qcontext ctx;
+  int id;
+  adns_query nqu;
+  adns_queryflags nflags;
+
+  dmstart= cbyte= *cbyte_io;
+  st= pap_domain(pai, &cbyte, max, &rrp->host,
+                pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0);
+  if (st) return st;
+  *cbyte_io= cbyte;
+
+  rrp->astatus= adns_s_ok;
+  rrp->naddrs= -1;
+  rrp->addrs= 0;
+
+  cbyte= pai->nsstart;
+
+  st= pap_findaddrs(pai, rrp, &cbyte, pai->nscount, dmstart);
+  if (st) return st;
+  if (rrp->naddrs != -1) return adns_s_ok;
+
+  st= pap_findaddrs(pai, rrp, &cbyte, pai->arcount, dmstart);
+  if (st) return st;
+  if (rrp->naddrs != -1) return adns_s_ok;
+
+  st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id,
+                           pai->dgram, pai->dglen, dmstart,
+                           adns_r_addr, adns_qf_quoteok_query);
+  if (st) return st;
+
+  ctx.ext= 0;
+  ctx.callback= icb_hostaddr;
+  ctx.info.hostaddr= rrp;
+  
+  nflags= adns_qf_quoteok_query;
+  if (!(pai->qu->flags & adns_qf_cname_loose)) nflags |= adns_qf_cname_forbid;
+  
+  st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr),
+                           &pai->qu->vb, id, nflags, pai->now, &ctx);
+  if (st) return st;
+
+  nqu->parent= pai->qu;
+  LIST_LINK_TAIL_PART(pai->qu->children,nqu,siblings.);
+
+  return adns_s_ok;
+}
+
+static adns_status pa_hostaddr(const parseinfo *pai, int cbyte, int max, void *datap) {
+  adns_rr_hostaddr *rrp= datap;
+  adns_status st;
+
+  st= pap_hostaddr(pai, &cbyte, max, rrp);
+  if (st) return st;
+  if (cbyte != max) return adns_s_invaliddata;
+
+  return adns_s_ok;
+}
+
+static int dip_hostaddr(adns_state ads, const adns_rr_hostaddr *ap, const adns_rr_hostaddr *bp) {
+  if (ap->astatus != bp->astatus) return ap->astatus;
+  if (ap->astatus) return 0;
+
+  assert(ap->addrs[0].addr.sa.sa_family == AF_INET);
+  assert(bp->addrs[0].addr.sa.sa_family == AF_INET);
+  return dip_inaddr(ads,
+                   ap->addrs[0].addr.inet.sin_addr,
+                   bp->addrs[0].addr.inet.sin_addr);
+}
+
+static int di_hostaddr(adns_state ads, const void *datap_a, const void *datap_b) {
+  const adns_rr_hostaddr *ap= datap_a, *bp= datap_b;
+
+  return dip_hostaddr(ads, ap,bp);
+}
+
+static void mfp_hostaddr(adns_query qu, adns_rr_hostaddr *rrp) {
+  void *tablev;
+
+  adns__makefinal_str(qu,&rrp->host);
+  tablev= rrp->addrs;
+  adns__makefinal_block(qu, &tablev, rrp->naddrs*sizeof(*rrp->addrs));
+  rrp->addrs= tablev;
+}
+
+static void mf_hostaddr(adns_query qu, void *datap) {
+  adns_rr_hostaddr *rrp= datap;
+
+  mfp_hostaddr(qu,rrp);
+}
+
+static adns_status csp_hostaddr(vbuf *vb, const adns_rr_hostaddr *rrp) {
+  const char *errstr;
+  adns_status st;
+  char buf[20];
+  int i;
+
+  st= csp_domain(vb,rrp->host);  if (st) return st;
+
+  CSP_ADDSTR(" ");
+  CSP_ADDSTR(adns_errtypeabbrev(rrp->astatus));
+
+  sprintf(buf," %d ",rrp->astatus);
+  CSP_ADDSTR(buf);
+
+  CSP_ADDSTR(adns_errabbrev(rrp->astatus));
+  CSP_ADDSTR(" ");
+
+  errstr= adns_strerror(rrp->astatus);
+  st= csp_qstring(vb,errstr,strlen(errstr));  if (st) return st;
+  
+  if (rrp->naddrs >= 0) {
+    CSP_ADDSTR(" (");
+    for (i=0; i<rrp->naddrs; i++) {
+      CSP_ADDSTR(" ");
+      st= csp_addr(vb,&rrp->addrs[i]);
+    }
+    CSP_ADDSTR(" )");
+  } else {
+    CSP_ADDSTR(" ?");
+  }
+  return adns_s_ok;
+}
+
+static adns_status cs_hostaddr(vbuf *vb, const void *datap) {
+  const adns_rr_hostaddr *rrp= datap;
+
+  return csp_hostaddr(vb,rrp);
+}
+
+/*
+ * _mx_raw   (pa,di)
+ */
+
+static adns_status pa_mx_raw(const parseinfo *pai, int cbyte, int max, void *datap) {
+  const byte *dgram= pai->dgram;
+  adns_rr_intstr *rrp= datap;
+  adns_status st;
+  int pref;
+
+  if (cbyte+2 > max) return adns_s_invaliddata;
+  GET_W(cbyte,pref);
+  rrp->i= pref;
+  st= pap_domain(pai, &cbyte, max, &rrp->str,
+                pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0);
+  if (st) return st;
+  
+  if (cbyte != max) return adns_s_invaliddata;
+  return adns_s_ok;
+}
+
+static int di_mx_raw(adns_state ads, const void *datap_a, const void *datap_b) {
+  const adns_rr_intstr *ap= datap_a, *bp= datap_b;
+
+  if (ap->i < bp->i) return 0;
+  if (ap->i > bp->i) return 1;
+  return 0;
+}
+
+/*
+ * _mx   (pa,di)
+ */
+
+static adns_status pa_mx(const parseinfo *pai, int cbyte, int max, void *datap) {
+  const byte *dgram= pai->dgram;
+  adns_rr_inthostaddr *rrp= datap;
+  adns_status st;
+  int pref;
+
+  if (cbyte+2 > max) return adns_s_invaliddata;
+  GET_W(cbyte,pref);
+  rrp->i= pref;
+  st= pap_hostaddr(pai, &cbyte, max, &rrp->ha);
+  if (st) return st;
+  
+  if (cbyte != max) return adns_s_invaliddata;
+  return adns_s_ok;
+}
+
+static int di_mx(adns_state ads, const void *datap_a, const void *datap_b) {
+  const adns_rr_inthostaddr *ap= datap_a, *bp= datap_b;
+
+  if (ap->i < bp->i) return 0;
+  if (ap->i > bp->i) return 1;
+  return dip_hostaddr(ads, &ap->ha, &bp->ha);
+}
+
+/*
+ * _inthostaddr  (mf,cs)
+ */
+
+static void mf_inthostaddr(adns_query qu, void *datap) {
+  adns_rr_inthostaddr *rrp= datap;
+
+  mfp_hostaddr(qu,&rrp->ha);
+}
+
+static adns_status cs_inthostaddr(vbuf *vb, const void *datap) {
+  const adns_rr_inthostaddr *rrp= datap;
+  char buf[10];
+
+  sprintf(buf,"%u ",rrp->i);
+  CSP_ADDSTR(buf);
+
+  return csp_hostaddr(vb,&rrp->ha);
+}
+
+/*
+ * _inthost  (cs)
+ */
+
+static adns_status cs_inthost(vbuf *vb, const void *datap) {
+  const adns_rr_intstr *rrp= datap;
+  char buf[10];
+
+  sprintf(buf,"%u ",rrp->i);
+  CSP_ADDSTR(buf);
+  return csp_domain(vb,rrp->str);
+}
+
+/*
+ * _ptr   (pa, +icb_ptr)
+ */
+
+static void icb_ptr(adns_query parent, adns_query child) {
+  adns_answer *cans= child->answer;
+  const adns_rr_addr *queried, *found;
+  adns_state ads= parent->ads;
+  int i;
+
+  if (cans->status == adns_s_nxdomain || cans->status == adns_s_nodata) {
+    adns__query_fail(parent,adns_s_inconsistent);
+    return;
+  } else if (cans->status) {
+    adns__query_fail(parent,cans->status);
+    return;
+  }
+
+  queried= &parent->ctx.info.ptr_parent_addr;
+  for (i=0, found=cans->rrs.addr; i<cans->nrrs; i++, found++) {
+    if (queried->len == found->len &&
+       !memcmp(&queried->addr,&found->addr,queried->len)) {
+      if (!parent->children.head) {
+       adns__query_done(parent);
+       return;
+      } else {
+       LIST_LINK_TAIL(ads->childw,parent);
+       return;
+      }
+    }
+  }
+
+  adns__query_fail(parent,adns_s_inconsistent);
+}
+
+static adns_status pa_ptr(const parseinfo *pai, int dmstart, int max, void *datap) {
+  static const char *(expectdomain[])= { DNS_INADDR_ARPA };
+  
+  char **rrp= datap;
+  adns_status st;
+  adns_rr_addr *ap;
+  findlabel_state fls;
+  char *ep;
+  byte ipv[4];
+  char labbuf[4];
+  int cbyte, i, lablen, labstart, l, id;
+  adns_query nqu;
+  qcontext ctx;
+
+  cbyte= dmstart;
+  st= pap_domain(pai, &cbyte, max, rrp,
+                pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0);
+  if (st) return st;
+  if (cbyte != max) return adns_s_invaliddata;
+
+  ap= &pai->qu->ctx.info.ptr_parent_addr;
+  if (!ap->len) {
+    adns__findlabel_start(&fls, pai->ads, -1, pai->qu,
+                         pai->qu->query_dgram, pai->qu->query_dglen,
+                         pai->qu->query_dglen, DNS_HDRSIZE, 0);
+    for (i=0; i<4; i++) {
+      st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
+      if (lablen<=0 || lablen>3) return adns_s_querydomainwrong;
+      memcpy(labbuf, pai->qu->query_dgram + labstart, lablen);  labbuf[lablen]= 0;
+      ipv[3-i]= strtoul(labbuf,&ep,10);  if (*ep) return adns_s_querydomainwrong;
+      if (lablen>1 && pai->qu->query_dgram[labstart]=='0')
+       return adns_s_querydomainwrong;
+    }
+    for (i=0; i<sizeof(expectdomain)/sizeof(*expectdomain); i++) {
+      st= adns__findlabel_next(&fls,&lablen,&labstart); assert(!st);
+      l= strlen(expectdomain[i]);
+      if (lablen != l || memcmp(pai->qu->query_dgram + labstart, expectdomain[i], l))
+       return adns_s_querydomainwrong;
+    }
+    st= adns__findlabel_next(&fls,&lablen,0); assert(!st);
+    if (lablen) return adns_s_querydomainwrong;
+    
+    ap->len= sizeof(struct sockaddr_in);
+    memset(&ap->addr,0,sizeof(ap->addr.inet));
+    ap->addr.inet.sin_family= AF_INET;
+    ap->addr.inet.sin_addr.s_addr=
+      htonl((ipv[0]<<24) | (ipv[1]<<16) | (ipv[2]<<8) | (ipv[3]));
+  }
+
+  st= adns__mkquery_frdgram(pai->ads, &pai->qu->vb, &id,
+                           pai->dgram, pai->dglen, dmstart,
+                           adns_r_addr, adns_qf_quoteok_query);
+  if (st) return st;
+
+  ctx.ext= 0;
+  ctx.callback= icb_ptr;
+  memset(&ctx.info,0,sizeof(ctx.info));
+  st= adns__internal_submit(pai->ads, &nqu, adns__findtype(adns_r_addr),
+                           &pai->qu->vb, id,
+                           adns_qf_quoteok_query, pai->now, &ctx);
+  if (st) return st;
+
+  nqu->parent= pai->qu;
+  LIST_LINK_TAIL_PART(pai->qu->children,nqu,siblings.);
+  return adns_s_ok;
+}
+
+/*
+ * _strpair   (mf)
+ */
+
+static void mf_strpair(adns_query qu, void *datap) {
+  adns_rr_strpair *rrp= datap;
+
+  adns__makefinal_str(qu,&rrp->array[0]);
+  adns__makefinal_str(qu,&rrp->array[1]);
+}
+
+/*
+ * _intstrpair   (mf)
+ */
+
+static void mf_intstrpair(adns_query qu, void *datap) {
+  adns_rr_intstrpair *rrp= datap;
+
+  adns__makefinal_str(qu,&rrp->array[0].str);
+  adns__makefinal_str(qu,&rrp->array[1].str);
+}
+
+/*
+ * _hinfo   (pa)
+ */
+
+static adns_status pa_hinfo(const parseinfo *pai, int cbyte, int max, void *datap) {
+  adns_rr_intstrpair *rrp= datap;
+  adns_status st;
+  int i;
+
+  for (i=0; i<2; i++) {
+    st= pap_qstring(pai, &cbyte, max, &rrp->array[i].i, &rrp->array[i].str);
+    if (st) return st;
+  }
+
+  if (cbyte != max) return adns_s_invaliddata;
+  
+  return adns_s_ok;
+}
+
+/*
+ * _mailbox   (pap,cs)
+ */
+
+static adns_status pap_mailbox822(const parseinfo *pai, int *cbyte_io, int max,
+                                 char **mb_r) {
+  int lablen, labstart, i, needquote, c, r, neednorm;
+  const unsigned char *p;
+  char *str;
+  findlabel_state fls;
+  adns_status st;
+  vbuf *vb;
+
+  vb= &pai->qu->vb;
+  vb->used= 0;
+  adns__findlabel_start(&fls, pai->ads,
+                       -1, pai->qu,
+                       pai->dgram, pai->dglen, max,
+                       *cbyte_io, cbyte_io);
+  st= adns__findlabel_next(&fls,&lablen,&labstart);
+  if (!lablen) {
+    adns__vbuf_appendstr(vb,".");
+    goto x_ok;
+  }
+
+  neednorm= 1;
+  for (i=0, needquote=0, p= pai->dgram+labstart; i<lablen; i++) {
+    c= *p++;
+    if ((c&~128) < 32 || (c&~128) == 127) return adns_s_invaliddata;
+    if (c == '.' && !neednorm) neednorm= 1;
+    else if (c==' ' || c>=127 || ctype_822special(c)) needquote++;
+    else neednorm= 0;
+  }
+
+  if (needquote || neednorm) {
+    r= adns__vbuf_ensure(vb, lablen+needquote+4); if (!r) R_NOMEM;
+    adns__vbuf_appendq(vb,"\"",1);
+    for (i=0, needquote=0, p= pai->dgram+labstart; i<lablen; i++, p++) {
+      c= *p;
+      if (c == '"' || c=='\\') adns__vbuf_appendq(vb,"\\",1);
+      adns__vbuf_appendq(vb,p,1);
+    }
+    adns__vbuf_appendq(vb,"\"",1);
+  } else {
+    r= adns__vbuf_append(vb, pai->dgram+labstart, lablen); if (!r) R_NOMEM;
+  }
+
+  r= adns__vbuf_appendstr(vb,"@"); if (!r) R_NOMEM;
+
+  st= adns__parse_domain_more(&fls,pai->ads, pai->qu,vb,0, pai->dgram);
+  if (st) return st;
+
+ x_ok:
+  str= adns__alloc_interim(pai->qu, vb->used+1); if (!str) R_NOMEM;
+  memcpy(str,vb->buf,vb->used);
+  str[vb->used]= 0;
+  *mb_r= str;
+  return adns_s_ok;
+}
+
+static adns_status pap_mailbox(const parseinfo *pai, int *cbyte_io, int max,
+                              char **mb_r) {
+  if (pai->qu->typei->type & adns__qtf_mail822) {
+    return pap_mailbox822(pai, cbyte_io, max, mb_r);
+  } else {
+    return pap_domain(pai, cbyte_io, max, mb_r, pdf_quoteok);
+  }
+}
+
+static adns_status csp_mailbox(vbuf *vb, const char *mailbox) {
+  return csp_domain(vb,mailbox);
+}
+
+/*
+ * _rp   (pa,cs)
+ */
+
+static adns_status pa_rp(const parseinfo *pai, int cbyte, int max, void *datap) {
+  adns_rr_strpair *rrp= datap;
+  adns_status st;
+
+  st= pap_mailbox(pai, &cbyte, max, &rrp->array[0]);
+  if (st) return st;
+
+  st= pap_domain(pai, &cbyte, max, &rrp->array[1], pdf_quoteok);
+  if (st) return st;
+
+  if (cbyte != max) return adns_s_invaliddata;
+  return adns_s_ok;
+}
+
+static adns_status cs_rp(vbuf *vb, const void *datap) {
+  const adns_rr_strpair *rrp= datap;
+  adns_status st;
+
+  st= csp_mailbox(vb,rrp->array[0]);  if (st) return st;
+  CSP_ADDSTR(" ");
+  st= csp_domain(vb,rrp->array[1]);  if (st) return st;
+
+  return adns_s_ok;
+}  
+
+/*
+ * _soa   (pa,mf,cs)
+ */
+
+static adns_status pa_soa(const parseinfo *pai, int cbyte, int max, void *datap) {
+  adns_rr_soa *rrp= datap;
+  const byte *dgram= pai->dgram;
+  adns_status st;
+  int msw, lsw, i;
+
+  st= pap_domain(pai, &cbyte, max, &rrp->mname,
+                pai->qu->flags & adns_qf_quoteok_anshost ? pdf_quoteok : 0);
+  if (st) return st;
+
+  st= pap_mailbox(pai, &cbyte, max, &rrp->rname);
+  if (st) return st;
+
+  if (cbyte+20 != max) return adns_s_invaliddata;
+  
+  for (i=0; i<5; i++) {
+    GET_W(cbyte,msw);
+    GET_W(cbyte,lsw);
+    (&rrp->serial)[i]= (msw<<16) | lsw;
+  }
+
+  return adns_s_ok;
+}
+
+static void mf_soa(adns_query qu, void *datap) {
+  adns_rr_soa *rrp= datap;
+
+  adns__makefinal_str(qu,&rrp->mname);
+  adns__makefinal_str(qu,&rrp->rname);
+}
+
+static adns_status cs_soa(vbuf *vb, const void *datap) {
+  const adns_rr_soa *rrp= datap;
+  char buf[20];
+  int i;
+  adns_status st;
+  
+  st= csp_domain(vb,rrp->mname);  if (st) return st;
+  CSP_ADDSTR(" ");
+  st= csp_mailbox(vb,rrp->rname);  if (st) return st;
+
+  for (i=0; i<5; i++) {
+    sprintf(buf," %lu",(&rrp->serial)[i]);
+    CSP_ADDSTR(buf);
+  }
+
+  return adns_s_ok;
+}
+
+/*
+ * _flat   (mf)
+ */
+
+static void mf_flat(adns_query qu, void *data) { }
+
+/*
+ * Now the table.
+ */
+
+#define TYPESZ_M(member)           (sizeof(*((adns_answer*)0)->rrs.member))
+
+#define DEEP_MEMB(memb) TYPESZ_M(memb), mf_##memb, cs_##memb
+#define FLAT_MEMB(memb) TYPESZ_M(memb), mf_flat, cs_##memb
+
+#define DEEP_TYPE(code,rrt,fmt,memb,parser,comparer,printer) \
+ { adns_r_##code, rrt, fmt, TYPESZ_M(memb), mf_##memb, printer, parser, comparer }
+#define FLAT_TYPE(code,rrt,fmt,memb,parser,comparer,printer) \
+ { adns_r_##code, rrt, fmt, TYPESZ_M(memb), mf_flat, printer, parser, comparer }
+
+static const typeinfo typeinfos[] = {
+/* Must be in ascending order of rrtype ! */
+/* mem-mgmt code  rrt     fmt     member      parser      comparer    printer       */
+                                                                                      
+FLAT_TYPE(a,      "A",     0,     inaddr,     pa_inaddr,  di_inaddr,  cs_inaddr     ),
+DEEP_TYPE(ns_raw, "NS",   "raw",  str,        pa_host_raw,0,          cs_domain     ),
+DEEP_TYPE(cname,  "CNAME", 0,     str,        pa_dom_raw, 0,          cs_domain     ),
+DEEP_TYPE(soa_raw,"SOA",  "raw",  soa,        pa_soa,     0,          cs_soa        ),
+DEEP_TYPE(ptr_raw,"PTR",  "raw",  str,        pa_host_raw,0,          cs_domain     ),
+DEEP_TYPE(hinfo,  "HINFO", 0,     intstrpair, pa_hinfo,   0,          cs_hinfo      ),
+DEEP_TYPE(mx_raw, "MX",   "raw",  intstr,     pa_mx_raw,  di_mx_raw,  cs_inthost    ),
+DEEP_TYPE(txt,    "TXT",   0,     manyistr,   pa_txt,     0,          cs_txt        ),
+DEEP_TYPE(rp_raw, "RP",   "raw",  strpair,    pa_rp,      0,          cs_rp         ),
+                                                                                      
+FLAT_TYPE(addr,   "A",  "addr",   addr,       pa_addr,    di_addr,    cs_addr       ),
+DEEP_TYPE(ns,     "NS", "+addr",  hostaddr,   pa_hostaddr,di_hostaddr,cs_hostaddr   ),
+DEEP_TYPE(ptr,    "PTR","checked",str,        pa_ptr,     0,          cs_domain     ),
+DEEP_TYPE(mx,     "MX", "+addr",  inthostaddr,pa_mx,      di_mx,      cs_inthostaddr),
+                                                                             
+DEEP_TYPE(soa,    "SOA","822",    soa,        pa_soa,     0,          cs_soa        ),
+DEEP_TYPE(rp,     "RP", "822",    strpair,    pa_rp,      0,          cs_rp         ),
+};
+
+const typeinfo *adns__findtype(adns_rrtype type) {
+  const typeinfo *begin, *end, *mid;
+
+  begin= typeinfos;  end= typeinfos+(sizeof(typeinfos)/sizeof(typeinfo));
+
+  while (begin < end) {
+    mid= begin + ((end-begin)>>1);
+    if (mid->type == type) return mid;
+    if (type > mid->type) begin= mid+1;
+    else end= mid;
+  }
+  return 0;
+}
index f801d8fce49f4811e700fdaf22eb517b3630865e..eb4a914ca9d14d672e354e64bf4e134e7c03f5e4 100755 (executable)
--- a/configure
+++ b/configure
@@ -35,6 +35,8 @@ ac_help="$ac_help
   --disable-devpoll       Enable the /dev/poll-based engine"
 ac_help="$ac_help
   --disable-kqueue        Enable the kqueue-based engine"
+ac_help="$ac_help
+  --disable-adns          Disable adns resolver"
 ac_help="$ac_help
   --with-symlink=name     Name to give the symlink; if name is "no," no
                           symlink will be created."
@@ -571,7 +573,7 @@ fi
 
 
 echo $ac_n "checking for installation prefix""... $ac_c" 1>&6
-echo "configure:575: checking for installation prefix" >&5
+echo "configure:577: checking for installation prefix" >&5
 if eval "test \"`echo '$''{'unet_cv_prefix'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -614,7 +616,7 @@ else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
 fi
 
 echo $ac_n "checking host system type""... $ac_c" 1>&6
-echo "configure:618: checking host system type" >&5
+echo "configure:620: checking host system type" >&5
 
 host_alias=$host
 case "$host_alias" in
@@ -638,7 +640,7 @@ echo "$ac_t""$host" 1>&6
 # Extract the first word of "gcc", so it can be a program name with args.
 set dummy gcc; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:642: checking for $ac_word" >&5
+echo "configure:644: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -668,7 +670,7 @@ if test -z "$CC"; then
   # Extract the first word of "cc", so it can be a program name with args.
 set dummy cc; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:672: checking for $ac_word" >&5
+echo "configure:674: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -719,7 +721,7 @@ fi
       # Extract the first word of "cl", so it can be a program name with args.
 set dummy cl; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:723: checking for $ac_word" >&5
+echo "configure:725: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -751,7 +753,7 @@ fi
 fi
 
 echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
-echo "configure:755: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+echo "configure:757: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
 
 ac_ext=c
 # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
@@ -762,12 +764,12 @@ cross_compiling=$ac_cv_prog_cc_cross
 
 cat > conftest.$ac_ext << EOF
 
-#line 766 "configure"
+#line 768 "configure"
 #include "confdefs.h"
 
 main(){return(0);}
 EOF
-if { (eval echo configure:771: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:773: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   ac_cv_prog_cc_works=yes
   # If we can't run a trivial program, we are probably using a cross compiler.
   if (./conftest; exit) 2>/dev/null; then
@@ -793,12 +795,12 @@ if test $ac_cv_prog_cc_works = no; then
   { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
 fi
 echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
-echo "configure:797: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "configure:799: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
 cross_compiling=$ac_cv_prog_cc_cross
 
 echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
-echo "configure:802: checking whether we are using GNU C" >&5
+echo "configure:804: checking whether we are using GNU C" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -807,7 +809,7 @@ else
   yes;
 #endif
 EOF
-if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:811: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:813: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
   ac_cv_prog_gcc=yes
 else
   ac_cv_prog_gcc=no
@@ -826,7 +828,7 @@ ac_test_CFLAGS="${CFLAGS+set}"
 ac_save_CFLAGS="$CFLAGS"
 CFLAGS=
 echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
-echo "configure:830: checking whether ${CC-cc} accepts -g" >&5
+echo "configure:832: checking whether ${CC-cc} accepts -g" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -862,7 +864,7 @@ fi
 
 
 echo $ac_n "checking for ${CC-cc} option to accept ANSI C""... $ac_c" 1>&6
-echo "configure:866: checking for ${CC-cc} option to accept ANSI C" >&5
+echo "configure:868: checking for ${CC-cc} option to accept ANSI C" >&5
 if eval "test \"`echo '$''{'am_cv_prog_cc_stdc'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -878,7 +880,7 @@ for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__
 do
   CC="$ac_save_CC $ac_arg"
   cat > conftest.$ac_ext <<EOF
-#line 882 "configure"
+#line 884 "configure"
 #include "confdefs.h"
 #include <stdarg.h>
 #include <stdio.h>
@@ -915,7 +917,7 @@ return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
 
 ; return 0; }
 EOF
-if { (eval echo configure:919: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:921: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   am_cv_prog_cc_stdc="$ac_arg"; break
 else
@@ -980,14 +982,14 @@ fi
 
 
 echo $ac_n "checking for library containing crypt""... $ac_c" 1>&6
-echo "configure:984: checking for library containing crypt" >&5
+echo "configure:986: checking for library containing crypt" >&5
 if eval "test \"`echo '$''{'ac_cv_search_crypt'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   ac_func_search_save_LIBS="$LIBS"
 ac_cv_search_crypt="no"
 cat > conftest.$ac_ext <<EOF
-#line 991 "configure"
+#line 993 "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
@@ -998,7 +1000,7 @@ int main() {
 crypt()
 ; return 0; }
 EOF
-if { (eval echo configure:1002: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1004: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   ac_cv_search_crypt="none required"
 else
@@ -1009,7 +1011,7 @@ rm -f conftest*
 test "$ac_cv_search_crypt" = "no" && for i in descrypt crypt; do
 LIBS="-l$i  $ac_func_search_save_LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1013 "configure"
+#line 1015 "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
@@ -1020,7 +1022,7 @@ int main() {
 crypt()
 ; return 0; }
 EOF
-if { (eval echo configure:1024: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1026: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   ac_cv_search_crypt="-l$i"
 break
@@ -1045,12 +1047,12 @@ fi
    # Most operating systems have gethostbyname() in the default searched
    # libraries (i.e. libc):
    echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6
-echo "configure:1049: checking for gethostbyname" >&5
+echo "configure:1051: checking for gethostbyname" >&5
 if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1054 "configure"
+#line 1056 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char gethostbyname(); below.  */
@@ -1073,7 +1075,7 @@ gethostbyname();
 
 ; return 0; }
 EOF
-if { (eval echo configure:1077: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1079: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_gethostbyname=yes"
 else
@@ -1092,7 +1094,7 @@ else
   echo "$ac_t""no" 1>&6
 # Some OSes (eg. Solaris) place it in libnsl:
      echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6
-echo "configure:1096: checking for gethostbyname in -lnsl" >&5
+echo "configure:1098: checking for gethostbyname in -lnsl" >&5
 ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1100,7 +1102,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lnsl  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1104 "configure"
+#line 1106 "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
@@ -1111,7 +1113,7 @@ int main() {
 gethostbyname()
 ; return 0; }
 EOF
-if { (eval echo configure:1115: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1117: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1138,7 +1140,7 @@ else
   echo "$ac_t""no" 1>&6
 # Some strange OSes (SINIX) have it in libsocket:
        echo $ac_n "checking for gethostbyname in -lsocket""... $ac_c" 1>&6
-echo "configure:1142: checking for gethostbyname in -lsocket" >&5
+echo "configure:1144: checking for gethostbyname in -lsocket" >&5
 ac_lib_var=`echo socket'_'gethostbyname | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1146,7 +1148,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lsocket  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1150 "configure"
+#line 1152 "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
@@ -1157,7 +1159,7 @@ int main() {
 gethostbyname()
 ; return 0; }
 EOF
-if { (eval echo configure:1161: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1163: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1186,7 +1188,7 @@ else
           # AC_CHECK_LIB's API is essentially broken so the following
           # ugliness is necessary:
           echo $ac_n "checking for gethostbyname in -lsocket""... $ac_c" 1>&6
-echo "configure:1190: checking for gethostbyname in -lsocket" >&5
+echo "configure:1192: checking for gethostbyname in -lsocket" >&5
 ac_lib_var=`echo socket'_'gethostbyname | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1194,7 +1196,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lsocket -lnsl $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1198 "configure"
+#line 1200 "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
@@ -1205,7 +1207,7 @@ int main() {
 gethostbyname()
 ; return 0; }
 EOF
-if { (eval echo configure:1209: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1211: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1224,7 +1226,7 @@ if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
 else
   echo "$ac_t""no" 1>&6
 echo $ac_n "checking for gethostbyname in -lresolv""... $ac_c" 1>&6
-echo "configure:1228: checking for gethostbyname in -lresolv" >&5
+echo "configure:1230: checking for gethostbyname in -lresolv" >&5
 ac_lib_var=`echo resolv'_'gethostbyname | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1232,7 +1234,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lresolv  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1236 "configure"
+#line 1238 "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
@@ -1243,7 +1245,7 @@ int main() {
 gethostbyname()
 ; return 0; }
 EOF
-if { (eval echo configure:1247: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1249: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1282,12 +1284,12 @@ fi
 fi
 
   echo $ac_n "checking for socket""... $ac_c" 1>&6
-echo "configure:1286: checking for socket" >&5
+echo "configure:1288: 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 1291 "configure"
+#line 1293 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char socket(); below.  */
@@ -1310,7 +1312,7 @@ socket();
 
 ; return 0; }
 EOF
-if { (eval echo configure:1314: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1316: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_socket=yes"
 else
@@ -1328,7 +1330,7 @@ if eval "test \"`echo '$ac_cv_func_'socket`\" = yes"; then
 else
   echo "$ac_t""no" 1>&6
 echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
-echo "configure:1332: checking for socket in -lsocket" >&5
+echo "configure:1334: 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
@@ -1336,7 +1338,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lsocket  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1340 "configure"
+#line 1342 "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
@@ -1347,7 +1349,7 @@ int main() {
 socket()
 ; return 0; }
 EOF
-if { (eval echo configure:1351: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1353: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1373,7 +1375,7 @@ EOF
 else
   echo "$ac_t""no" 1>&6
 echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
-echo "configure:1377: checking for socket in -lsocket" >&5
+echo "configure:1379: 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
@@ -1381,7 +1383,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lsocket -lnsl $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1385 "configure"
+#line 1387 "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
@@ -1392,7 +1394,7 @@ int main() {
 socket()
 ; return 0; }
 EOF
-if { (eval echo configure:1396: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1398: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1418,71 +1420,8 @@ fi
 
   
 
-
-echo $ac_n "checking for library containing res_mkquery""... $ac_c" 1>&6
-echo "configure:1424: checking for library containing res_mkquery" >&5
-if eval "test \"`echo '$''{'ac_cv_search_res_mkquery'+set}'`\" = set"; then
-  echo $ac_n "(cached) $ac_c" 1>&6
-else
-  ac_func_search_save_LIBS="$LIBS"
-ac_cv_search_res_mkquery="no"
-cat > conftest.$ac_ext <<EOF
-#line 1431 "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 res_mkquery();
-
-int main() {
-res_mkquery()
-; return 0; }
-EOF
-if { (eval echo configure:1442: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
-  rm -rf conftest*
-  ac_cv_search_res_mkquery="none required"
-else
-  echo "configure: failed program was:" >&5
-  cat conftest.$ac_ext >&5
-fi
-rm -f conftest*
-test "$ac_cv_search_res_mkquery" = "no" && for i in resolv; do
-LIBS="-l$i  $ac_func_search_save_LIBS"
-cat > conftest.$ac_ext <<EOF
-#line 1453 "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 res_mkquery();
-
-int main() {
-res_mkquery()
-; return 0; }
-EOF
-if { (eval echo configure:1464: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
-  rm -rf conftest*
-  ac_cv_search_res_mkquery="-l$i"
-break
-else
-  echo "configure: failed program was:" >&5
-  cat conftest.$ac_ext >&5
-fi
-rm -f conftest*
-done
-LIBS="$ac_func_search_save_LIBS"
-fi
-
-echo "$ac_t""$ac_cv_search_res_mkquery" 1>&6
-if test "$ac_cv_search_res_mkquery" != "no"; then
-  test "$ac_cv_search_res_mkquery" = "none required" || LIBS="$ac_cv_search_res_mkquery $LIBS"
-  
-else :
-  { echo "configure: error: Unable to find library containing res_mkquery()" 1>&2; exit 1; }
-fi
-
 echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
-echo "configure:1486: checking how to run the C preprocessor" >&5
+echo "configure:1425: checking how to run the C preprocessor" >&5
 # On Suns, sometimes $CPP names a directory.
 if test -n "$CPP" && test -d "$CPP"; then
   CPP=
@@ -1497,13 +1436,13 @@ else
   # On the NeXT, cc -E runs the code through the compiler's parser,
   # not just through cpp.
   cat > conftest.$ac_ext <<EOF
-#line 1501 "configure"
+#line 1440 "configure"
 #include "confdefs.h"
 #include <assert.h>
 Syntax Error
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1507: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1446: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   :
@@ -1514,13 +1453,13 @@ else
   rm -rf conftest*
   CPP="${CC-cc} -E -traditional-cpp"
   cat > conftest.$ac_ext <<EOF
-#line 1518 "configure"
+#line 1457 "configure"
 #include "confdefs.h"
 #include <assert.h>
 Syntax Error
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1524: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1463: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   :
@@ -1531,13 +1470,13 @@ else
   rm -rf conftest*
   CPP="${CC-cc} -nologo -E"
   cat > conftest.$ac_ext <<EOF
-#line 1535 "configure"
+#line 1474 "configure"
 #include "confdefs.h"
 #include <assert.h>
 Syntax Error
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1541: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1480: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   :
@@ -1562,12 +1501,12 @@ fi
 echo "$ac_t""$CPP" 1>&6
 
 echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
-echo "configure:1566: checking for ANSI C header files" >&5
+echo "configure:1505: checking for ANSI C header files" >&5
 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1571 "configure"
+#line 1510 "configure"
 #include "confdefs.h"
 #include <stdlib.h>
 #include <stdarg.h>
@@ -1575,7 +1514,7 @@ else
 #include <float.h>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1579: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1518: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -1592,7 +1531,7 @@ rm -f conftest*
 if test $ac_cv_header_stdc = yes; then
   # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
 cat > conftest.$ac_ext <<EOF
-#line 1596 "configure"
+#line 1535 "configure"
 #include "confdefs.h"
 #include <string.h>
 EOF
@@ -1610,7 +1549,7 @@ fi
 if test $ac_cv_header_stdc = yes; then
   # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
 cat > conftest.$ac_ext <<EOF
-#line 1614 "configure"
+#line 1553 "configure"
 #include "confdefs.h"
 #include <stdlib.h>
 EOF
@@ -1631,7 +1570,7 @@ if test "$cross_compiling" = yes; then
   :
 else
   cat > conftest.$ac_ext <<EOF
-#line 1635 "configure"
+#line 1574 "configure"
 #include "confdefs.h"
 #include <ctype.h>
 #define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
@@ -1642,7 +1581,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
 exit (0); }
 
 EOF
-if { (eval echo configure:1646: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:1585: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   :
 else
@@ -1669,17 +1608,17 @@ for ac_hdr in poll.h sys/devpoll.h sys/event.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:1673: checking for $ac_hdr" >&5
+echo "configure:1612: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1678 "configure"
+#line 1617 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1683: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1622: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -1707,14 +1646,14 @@ done
 
 
 echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
-echo "configure:1711: checking whether byte ordering is bigendian" >&5
+echo "configure:1650: checking whether byte ordering is bigendian" >&5
 if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   ac_cv_c_bigendian=unknown
 # See if sys/param.h defines the BYTE_ORDER macro.
 cat > conftest.$ac_ext <<EOF
-#line 1718 "configure"
+#line 1657 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/param.h>
@@ -1725,11 +1664,11 @@ int main() {
 #endif
 ; return 0; }
 EOF
-if { (eval echo configure:1729: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1668: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   # It does; now see whether it defined to BIG_ENDIAN or not.
 cat > conftest.$ac_ext <<EOF
-#line 1733 "configure"
+#line 1672 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/param.h>
@@ -1740,7 +1679,7 @@ int main() {
 #endif
 ; return 0; }
 EOF
-if { (eval echo configure:1744: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1683: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_bigendian=yes
 else
@@ -1760,7 +1699,7 @@ if test "$cross_compiling" = yes; then
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 1764 "configure"
+#line 1703 "configure"
 #include "confdefs.h"
 main () {
   /* Are we little or big endian?  From Harbison&Steele.  */
@@ -1773,7 +1712,7 @@ main () {
   exit (u.c[sizeof (long) - 1] == 1);
 }
 EOF
-if { (eval echo configure:1777: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:1716: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_c_bigendian=no
 else
@@ -1797,12 +1736,12 @@ EOF
 fi
 
 echo $ac_n "checking for size_t""... $ac_c" 1>&6
-echo "configure:1801: checking for size_t" >&5
+echo "configure:1740: checking for size_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1806 "configure"
+#line 1745 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -1830,12 +1769,12 @@ EOF
 fi
 
 echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
-echo "configure:1834: checking whether time.h and sys/time.h may both be included" >&5
+echo "configure:1773: checking whether time.h and sys/time.h may both be included" >&5
 if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1839 "configure"
+#line 1778 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/time.h>
@@ -1844,7 +1783,7 @@ int main() {
 struct tm *tp;
 ; return 0; }
 EOF
-if { (eval echo configure:1848: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1787: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_header_time=yes
 else
@@ -1865,12 +1804,12 @@ EOF
 fi
 
 echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6
-echo "configure:1869: checking whether struct tm is in sys/time.h or time.h" >&5
+echo "configure:1808: checking whether struct tm is in sys/time.h or time.h" >&5
 if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1874 "configure"
+#line 1813 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <time.h>
@@ -1878,7 +1817,7 @@ int main() {
 struct tm *tp; tp->tm_sec;
 ; return 0; }
 EOF
-if { (eval echo configure:1882: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1821: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_struct_tm=time.h
 else
@@ -1899,12 +1838,12 @@ EOF
 fi
 
 echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6
-echo "configure:1903: checking for uid_t in sys/types.h" >&5
+echo "configure:1842: checking for uid_t in sys/types.h" >&5
 if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1908 "configure"
+#line 1847 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 EOF
@@ -1933,7 +1872,7 @@ EOF
 fi
 
 echo $ac_n "checking size of short""... $ac_c" 1>&6
-echo "configure:1937: checking size of short" >&5
+echo "configure:1876: checking size of short" >&5
 if eval "test \"`echo '$''{'ac_cv_sizeof_short'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1941,18 +1880,18 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 1945 "configure"
+#line 1884 "configure"
 #include "confdefs.h"
 #include <stdio.h>
-main()
+int main()
 {
   FILE *f=fopen("conftestval", "w");
-  if (!f) exit(1);
+  if (!f) return(1);
   fprintf(f, "%d\n", sizeof(short));
-  exit(0);
+  return(0);
 }
 EOF
-if { (eval echo configure:1956: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:1895: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_sizeof_short=`cat conftestval`
 else
@@ -1972,7 +1911,7 @@ EOF
 
 
 echo $ac_n "checking size of int""... $ac_c" 1>&6
-echo "configure:1976: checking size of int" >&5
+echo "configure:1915: checking size of int" >&5
 if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1980,18 +1919,18 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 1984 "configure"
+#line 1923 "configure"
 #include "confdefs.h"
 #include <stdio.h>
-main()
+int main()
 {
   FILE *f=fopen("conftestval", "w");
-  if (!f) exit(1);
+  if (!f) return(1);
   fprintf(f, "%d\n", sizeof(int));
-  exit(0);
+  return(0);
 }
 EOF
-if { (eval echo configure:1995: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:1934: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_sizeof_int=`cat conftestval`
 else
@@ -2011,7 +1950,7 @@ EOF
 
 
 echo $ac_n "checking size of long""... $ac_c" 1>&6
-echo "configure:2015: checking size of long" >&5
+echo "configure:1954: checking size of long" >&5
 if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2019,18 +1958,18 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 2023 "configure"
+#line 1962 "configure"
 #include "confdefs.h"
 #include <stdio.h>
-main()
+int main()
 {
   FILE *f=fopen("conftestval", "w");
-  if (!f) exit(1);
+  if (!f) return(1);
   fprintf(f, "%d\n", sizeof(long));
-  exit(0);
+  return(0);
 }
 EOF
-if { (eval echo configure:2034: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:1973: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_sizeof_long=`cat conftestval`
 else
@@ -2050,7 +1989,7 @@ EOF
 
 
 echo $ac_n "checking size of void *""... $ac_c" 1>&6
-echo "configure:2054: checking size of void *" >&5
+echo "configure:1993: checking size of void *" >&5
 if eval "test \"`echo '$''{'ac_cv_sizeof_void_p'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2058,18 +1997,18 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 2062 "configure"
+#line 2001 "configure"
 #include "confdefs.h"
 #include <stdio.h>
-main()
+int main()
 {
   FILE *f=fopen("conftestval", "w");
-  if (!f) exit(1);
+  if (!f) return(1);
   fprintf(f, "%d\n", sizeof(void *));
-  exit(0);
+  return(0);
 }
 EOF
-if { (eval echo configure:2073: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:2012: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_sizeof_void_p=`cat conftestval`
 else
@@ -2090,12 +2029,12 @@ EOF
 
 if test "$ac_cv_sizeof_int" = 2 ; then
   echo $ac_n "checking for int16_t""... $ac_c" 1>&6
-echo "configure:2094: checking for int16_t" >&5
+echo "configure:2033: checking for int16_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_int16_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2099 "configure"
+#line 2038 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -2123,12 +2062,12 @@ EOF
 fi
 
   echo $ac_n "checking for u_int16_t""... $ac_c" 1>&6
-echo "configure:2127: checking for u_int16_t" >&5
+echo "configure:2066: checking for u_int16_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_u_int16_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2132 "configure"
+#line 2071 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -2157,12 +2096,12 @@ fi
 
 elif test "$ac_cv_sizeof_short" = 2 ; then
   echo $ac_n "checking for int16_t""... $ac_c" 1>&6
-echo "configure:2161: checking for int16_t" >&5
+echo "configure:2100: checking for int16_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_int16_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2166 "configure"
+#line 2105 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -2190,12 +2129,12 @@ EOF
 fi
 
   echo $ac_n "checking for u_int16_t""... $ac_c" 1>&6
-echo "configure:2194: checking for u_int16_t" >&5
+echo "configure:2133: checking for u_int16_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_u_int16_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2199 "configure"
+#line 2138 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -2227,12 +2166,12 @@ else
 fi
 if test "$ac_cv_sizeof_int" = 4 ; then
   echo $ac_n "checking for int32_t""... $ac_c" 1>&6
-echo "configure:2231: checking for int32_t" >&5
+echo "configure:2170: checking for int32_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_int32_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2236 "configure"
+#line 2175 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -2260,12 +2199,12 @@ EOF
 fi
 
   echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
-echo "configure:2264: checking for u_int32_t" >&5
+echo "configure:2203: checking for u_int32_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_u_int32_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2269 "configure"
+#line 2208 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -2294,12 +2233,12 @@ fi
 
 elif test "$ac_cv_sizeof_short" = 4 ; then
   echo $ac_n "checking for int32_t""... $ac_c" 1>&6
-echo "configure:2298: checking for int32_t" >&5
+echo "configure:2237: checking for int32_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_int32_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2303 "configure"
+#line 2242 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -2327,12 +2266,12 @@ EOF
 fi
 
   echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
-echo "configure:2331: checking for u_int32_t" >&5
+echo "configure:2270: checking for u_int32_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_u_int32_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2336 "configure"
+#line 2275 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -2361,12 +2300,12 @@ fi
 
 elif test "$ac_cv_sizeof_long" = 4 ; then
   echo $ac_n "checking for int32_t""... $ac_c" 1>&6
-echo "configure:2365: checking for int32_t" >&5
+echo "configure:2304: checking for int32_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_int32_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2370 "configure"
+#line 2309 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -2394,12 +2333,12 @@ EOF
 fi
 
   echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
-echo "configure:2398: checking for u_int32_t" >&5
+echo "configure:2337: checking for u_int32_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_u_int32_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2403 "configure"
+#line 2342 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -2433,12 +2372,12 @@ fi
 for ac_func in kqueue setrlimit getrusage times
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:2437: checking for $ac_func" >&5
+echo "configure:2376: 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 2442 "configure"
+#line 2381 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -2461,7 +2400,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:2465: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2404: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -2487,7 +2426,7 @@ done
 
 
 echo $ac_n "checking for restartable system calls""... $ac_c" 1>&6
-echo "configure:2491: checking for restartable system calls" >&5
+echo "configure:2430: checking for restartable system calls" >&5
 if eval "test \"`echo '$''{'ac_cv_sys_restartable_syscalls'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2495,7 +2434,7 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 2499 "configure"
+#line 2438 "configure"
 #include "confdefs.h"
 /* Exit 0 (true) if wait returns something other than -1,
    i.e. the pid of the child, which means that wait was restarted
@@ -2513,7 +2452,7 @@ main () {
 }
 
 EOF
-if { (eval echo configure:2517: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:2456: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_sys_restartable_syscalls=yes
 else
@@ -2537,7 +2476,7 @@ fi
 
 
 echo $ac_n "checking for donuts""... $ac_c" 1>&6
-echo "configure:2541: checking for donuts" >&5
+echo "configure:2480: checking for donuts" >&5
 echo "$ac_t""yes" 1>&6
 
 for ac_prog in gawk mawk nawk awk
@@ -2545,7 +2484,7 @@ do
 # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:2549: checking for $ac_word" >&5
+echo "configure:2488: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2575,7 +2514,7 @@ test -n "$AWK" && break
 done
 
 echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
-echo "configure:2579: checking whether ${MAKE-make} sets \${MAKE}" >&5
+echo "configure:2518: checking whether ${MAKE-make} sets \${MAKE}" >&5
 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2613,7 +2552,7 @@ fi
 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
 # ./install, which can be erroneously created by make from ./install.sh.
 echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
-echo "configure:2617: checking for a BSD compatible install" >&5
+echo "configure:2556: checking for a BSD compatible install" >&5
 if test -z "$INSTALL"; then
 if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2666,7 +2605,7 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
 test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
 
 echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6
-echo "configure:2670: checking whether ln -s works" >&5
+echo "configure:2609: checking whether ln -s works" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2691,7 +2630,7 @@ do
 # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:2695: checking for $ac_word" >&5
+echo "configure:2634: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_RMPROG'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2732,7 +2671,7 @@ do
 # Extract the first word of "$ac_prog", so it can be a program name with args.
 set dummy $ac_prog; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:2736: checking for $ac_word" >&5
+echo "configure:2675: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_SHPROG'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2770,7 +2709,7 @@ test -n "$SHPROG" || SHPROG="/bin/sh"
 
 
 echo $ac_n "checking for posix non-blocking""... $ac_c" 1>&6
-echo "configure:2774: checking for posix non-blocking" >&5
+echo "configure:2713: checking for posix non-blocking" >&5
 if eval "test \"`echo '$''{'unet_cv_sys_nonblocking_posix'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2778,7 +2717,7 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 2782 "configure"
+#line 2721 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -2804,7 +2743,7 @@ int main(void)
   exit(1);
 }
 EOF
-if { (eval echo configure:2808: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:2747: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   unet_cv_sys_nonblocking_posix=yes
 else
@@ -2826,7 +2765,7 @@ EOF
 
 else
 echo $ac_n "checking for bsd non-blocking""... $ac_c" 1>&6
-echo "configure:2830: checking for bsd non-blocking" >&5
+echo "configure:2769: checking for bsd non-blocking" >&5
 if eval "test \"`echo '$''{'unet_cv_sys_nonblocking_bsd'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2834,7 +2773,7 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 2838 "configure"
+#line 2777 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -2860,7 +2799,7 @@ int main(void)
   exit(1);
 }
 EOF
-if { (eval echo configure:2864: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:2803: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   unet_cv_sys_nonblocking_bsd=yes
 else
@@ -2888,19 +2827,19 @@ EOF
 fi
 fi
 echo $ac_n "checking for posix signals""... $ac_c" 1>&6
-echo "configure:2892: checking for posix signals" >&5
+echo "configure:2831: checking for posix signals" >&5
 if eval "test \"`echo '$''{'unet_cv_sys_signal_posix'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2897 "configure"
+#line 2836 "configure"
 #include "confdefs.h"
 #include <signal.h>
 int main() {
 sigaction(SIGTERM, (struct sigaction *)0L, (struct sigaction *)0L)
 ; return 0; }
 EOF
-if { (eval echo configure:2904: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2843: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   unet_cv_sys_signal_posix=yes
 else
@@ -2920,7 +2859,7 @@ EOF
 
 else
 echo $ac_n "checking for bsd reliable signals""... $ac_c" 1>&6
-echo "configure:2924: checking for bsd reliable signals" >&5
+echo "configure:2863: checking for bsd reliable signals" >&5
 if eval "test \"`echo '$''{'unet_cv_sys_signal_bsd'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2928,7 +2867,7 @@ else
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 2932 "configure"
+#line 2871 "configure"
 #include "confdefs.h"
 #include <signal.h>
 int calls = 0;
@@ -2946,7 +2885,7 @@ int main(void)
   exit (0);
 }
 EOF
-if { (eval echo configure:2950: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:2889: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   unet_cv_sys_signal_bsd=yes
 else
@@ -2975,20 +2914,20 @@ fi
 fi
 
 echo $ac_n "checking if the compiler understands -pipe""... $ac_c" 1>&6
-echo "configure:2979: checking if the compiler understands -pipe" >&5
+echo "configure:2918: checking if the compiler understands -pipe" >&5
 unet_cv_pipe_flags="$ac_cv_prog_gcc"
 if test "$ac_cv_prog_gcc" = no; then
   OLDCFLAGS="$CFLAGS"
   CFLAGS="$CFLAGS -pipe"
   cat > conftest.$ac_ext <<EOF
-#line 2985 "configure"
+#line 2924 "configure"
 #include "confdefs.h"
 
 int main() {
 
 ; return 0; }
 EOF
-if { (eval echo configure:2992: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2931: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   unet_cv_pipe_flags=yes
 else
@@ -3008,7 +2947,7 @@ fi
 
 
 echo $ac_n "checking for OS-dependent information""... $ac_c" 1>&6
-echo "configure:3012: checking for OS-dependent information" >&5
+echo "configure:2951: checking for OS-dependent information" >&5
 case "$host" in
     *-linux*)
        echo "$ac_t""Linux ($host) found." 1>&6
@@ -3068,7 +3007,7 @@ esac
 
 
 echo $ac_n "checking whether to enable use of poll()""... $ac_c" 1>&6
-echo "configure:3072: checking whether to enable use of poll()" >&5
+echo "configure:3011: checking whether to enable use of poll()" >&5
 # Check whether --enable-poll or --disable-poll was given.
 if test "${enable_poll+set}" = set; then
   enableval="$enable_poll"
@@ -3102,7 +3041,7 @@ fi
 
 
 echo $ac_n "checking whether to enable debug mode""... $ac_c" 1>&6
-echo "configure:3106: checking whether to enable debug mode" >&5
+echo "configure:3045: checking whether to enable debug mode" >&5
 # Check whether --enable-debug or --disable-debug was given.
 if test "${enable_debug+set}" = set; then
   enableval="$enable_debug"
@@ -3126,7 +3065,7 @@ EOF
 fi
 
 echo $ac_n "checking whether to enable asserts""... $ac_c" 1>&6
-echo "configure:3130: checking whether to enable asserts" >&5
+echo "configure:3069: checking whether to enable asserts" >&5
 # Check whether --enable-asserts or --disable-asserts was given.
 if test "${enable_asserts+set}" = set; then
   enableval="$enable_asserts"
@@ -3150,7 +3089,7 @@ EOF
 fi
 
 echo $ac_n "checking whether to enable debugging symbols""... $ac_c" 1>&6
-echo "configure:3154: checking whether to enable debugging symbols" >&5
+echo "configure:3093: checking whether to enable debugging symbols" >&5
 # Check whether --enable-symbols or --disable-symbols was given.
 if test "${enable_symbols+set}" = set; then
   enableval="$enable_symbols"
@@ -3171,7 +3110,7 @@ if test x"$unet_cv_enable_symbols" = xyes; then
 fi
 
 echo $ac_n "checking whether to enable head in sand 'features'""... $ac_c" 1>&6
-echo "configure:3175: checking whether to enable head in sand 'features'" >&5
+echo "configure:3114: checking whether to enable head in sand 'features'" >&5
 # Check whether --enable-headinsand or --disable-headinsand was given.
 if test "${enable_headinsand+set}" = set; then
   enableval="$enable_headinsand"
@@ -3192,7 +3131,7 @@ if test x"$unet_cv_enable_headinsand" = xno; then
 fi
 
 echo $ac_n "checking whether to enable profiling support (gprof)""... $ac_c" 1>&6
-echo "configure:3196: checking whether to enable profiling support (gprof)" >&5
+echo "configure:3135: checking whether to enable profiling support (gprof)" >&5
 # Check whether --enable-profile or --disable-profile was given.
 if test "${enable_profile+set}" = set; then
   enableval="$enable_profile"
@@ -3213,7 +3152,7 @@ if test x"$unet_cv_enable_profile" = xyes; then
 fi
 
 echo $ac_n "checking whether to enable pedantic compiler warnings""... $ac_c" 1>&6
-echo "configure:3217: checking whether to enable pedantic compiler warnings" >&5
+echo "configure:3156: checking whether to enable pedantic compiler warnings" >&5
 # Check whether --enable-pedantic or --disable-pedantic was given.
 if test "${enable_pedantic+set}" = set; then
   enableval="$enable_pedantic"
@@ -3234,7 +3173,7 @@ if test x"$unet_cv_enable_pedantic" = xyes; then
 fi
 
 echo $ac_n "checking whether to enable compiler warnings""... $ac_c" 1>&6
-echo "configure:3238: checking whether to enable compiler warnings" >&5
+echo "configure:3177: checking whether to enable compiler warnings" >&5
 # Check whether --enable-warnings or --disable-warnings was given.
 if test "${enable_warnings+set}" = set; then
   enableval="$enable_warnings"
@@ -3255,7 +3194,7 @@ if test x"$unet_cv_enable_warnings" = xyes; then
 fi
 
 echo $ac_n "checking whether to enable inlining for a few critical functions""... $ac_c" 1>&6
-echo "configure:3259: checking whether to enable inlining for a few critical functions" >&5
+echo "configure:3198: checking whether to enable inlining for a few critical functions" >&5
 # Check whether --enable-inlines or --disable-inlines was given.
 if test "${enable_inlines+set}" = set; then
   enableval="$enable_inlines"
@@ -3279,7 +3218,7 @@ EOF
 fi
 
 echo $ac_n "checking whether to enable the /dev/poll event engine""... $ac_c" 1>&6
-echo "configure:3283: checking whether to enable the /dev/poll event engine" >&5
+echo "configure:3222: checking whether to enable the /dev/poll event engine" >&5
 # Check whether --enable-devpoll or --disable-devpoll was given.
 if test "${enable_devpoll+set}" = set; then
   enableval="$enable_devpoll"
@@ -3309,7 +3248,7 @@ EOF
 fi
 
 echo $ac_n "checking whether to enable the kqueue event engine""... $ac_c" 1>&6
-echo "configure:3313: checking whether to enable the kqueue event engine" >&5
+echo "configure:3252: checking whether to enable the kqueue event engine" >&5
 # Check whether --enable-kqueue or --disable-kqueue was given.
 if test "${enable_kqueue+set}" = set; then
   enableval="$enable_kqueue"
@@ -3338,8 +3277,106 @@ EOF
     ENGINE_C="engine_kqueue.c $ENGINE_C"
 fi
 
+echo $ac_n "checking whether to enable the adns resolver engine""... $ac_c" 1>&6
+echo "configure:3282: checking whether to enable the adns resolver engine" >&5
+# Check whether --enable-adns or --disable-adns was given.
+if test "${enable_adns+set}" = set; then
+  enableval="$enable_adns"
+  unet_cv_enable_adns=$enable_adns
+else
+  if eval "test \"`echo '$''{'unet_cv_enable_adns'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  unet_cv_enable_adns=yes
+fi
+
+fi
+
+
+echo "$ac_t""$unet_cv_enable_adns" 1>&6
+
+if test x"$unet_cv_enable_adns" != xno; then
+    cat >> confdefs.h <<\EOF
+#define USE_ADNS 
+EOF
+
+    subdirs="adns"
+
+    RES_C="res_adns.c"
+    LIBS="../adns/src/libadns.a $LIBS"
+    ADNS_SUBDIR="adns"
+else
+    RES_C="res_libresolv.c"
+    ADNS_SUBDIR=""
+
+            
+echo $ac_n "checking for library containing res_mkquery""... $ac_c" 1>&6
+echo "configure:3315: checking for library containing res_mkquery" >&5
+if eval "test \"`echo '$''{'ac_cv_search_res_mkquery'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_func_search_save_LIBS="$LIBS"
+ac_cv_search_res_mkquery="no"
+cat > conftest.$ac_ext <<EOF
+#line 3322 "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 res_mkquery();
+
+int main() {
+res_mkquery()
+; return 0; }
+EOF
+if { (eval echo configure:3333: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  ac_cv_search_res_mkquery="none required"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+test "$ac_cv_search_res_mkquery" = "no" && for i in resolv; do
+LIBS="-l$i  $ac_func_search_save_LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 3344 "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 res_mkquery();
+
+int main() {
+res_mkquery()
+; return 0; }
+EOF
+if { (eval echo configure:3355: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  ac_cv_search_res_mkquery="-l$i"
+break
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+done
+LIBS="$ac_func_search_save_LIBS"
+fi
+
+echo "$ac_t""$ac_cv_search_res_mkquery" 1>&6
+if test "$ac_cv_search_res_mkquery" != "no"; then
+  test "$ac_cv_search_res_mkquery" = "none required" || LIBS="$ac_cv_search_res_mkquery $LIBS"
+  
+else :
+  { echo "configure: error: Unable to find library containing res_mkquery()" 1>&2; exit 1; }
+fi
+fi
+
+
+
 echo $ac_n "checking what name to give the symlink""... $ac_c" 1>&6
-echo "configure:3343: checking what name to give the symlink" >&5
+echo "configure:3380: checking what name to give the symlink" >&5
 # Check whether --with-symlink or --without-symlink was given.
 if test "${with_symlink+set}" = set; then
   withval="$with_symlink"
@@ -3371,7 +3408,7 @@ fi
 
 
 echo $ac_n "checking what permissions to set on the installed binary""... $ac_c" 1>&6
-echo "configure:3375: checking what permissions to set on the installed binary" >&5
+echo "configure:3412: checking what permissions to set on the installed binary" >&5
 # Check whether --with-mode or --without-mode was given.
 if test "${with_mode+set}" = set; then
   withval="$with_mode"
@@ -3397,7 +3434,7 @@ IRCDMODE=$unet_cv_with_mode
 
 unet_uid=`id | sed -e 's/.*uid=[0-9]*(//' -e 's/).*//' 2> /dev/null`
 echo $ac_n "checking which user should own the installed binary""... $ac_c" 1>&6
-echo "configure:3401: checking which user should own the installed binary" >&5
+echo "configure:3438: checking which user should own the installed binary" >&5
 # Check whether --with-owner or --without-owner was given.
 if test "${with_owner+set}" = set; then
   withval="$with_owner"
@@ -3423,7 +3460,7 @@ IRCDOWN=$unet_cv_with_owner
 
 unet_gid=`id | sed -e 's/.*gid=[0-9]*(//' -e 's/).*//' 2> /dev/null`
 echo $ac_n "checking which group should own the installed binary""... $ac_c" 1>&6
-echo "configure:3427: checking which group should own the installed binary" >&5
+echo "configure:3464: checking which group should own the installed binary" >&5
 # Check whether --with-group or --without-group was given.
 if test "${with_group+set}" = set; then
   withval="$with_group"
@@ -3455,7 +3492,7 @@ if test -f /etc/resolv.conf; then
     fi
 fi
 echo $ac_n "checking for site domain name""... $ac_c" 1>&6
-echo "configure:3459: checking for site domain name" >&5
+echo "configure:3496: checking for site domain name" >&5
 # Check whether --with-domain or --without-domain was given.
 if test "${with_domain+set}" = set; then
   withval="$with_domain"
@@ -3485,7 +3522,7 @@ EOF
 
 
 echo $ac_n "checking if chroot operation is desired""... $ac_c" 1>&6
-echo "configure:3489: checking if chroot operation is desired" >&5
+echo "configure:3526: checking if chroot operation is desired" >&5
 # Check whether --with-chroot or --without-chroot was given.
 if test "${with_chroot+set}" = set; then
   withval="$with_chroot"
@@ -3533,7 +3570,7 @@ prefix=$unet_save_prefix
 exec_prefix=$unet_save_exec_prefix
 
 echo $ac_n "checking where the binary will be for /restart""... $ac_c" 1>&6
-echo "configure:3537: checking where the binary will be for /restart" >&5
+echo "configure:3574: checking where the binary will be for /restart" >&5
 if test x"$unet_cv_with_symlink" = xno; then
     unet_spath="$unet_bindir/ircd"
 else
@@ -3555,7 +3592,7 @@ EOF
 
 
 echo $ac_n "checking what the data directory should be""... $ac_c" 1>&6
-echo "configure:3559: checking what the data directory should be" >&5
+echo "configure:3596: checking what the data directory should be" >&5
 # Check whether --with-dpath or --without-dpath was given.
 if test "${with_dpath+set}" = set; then
   withval="$with_dpath"
@@ -3598,7 +3635,7 @@ DPATH=$unet_cv_with_dpath
 
 
 echo $ac_n "checking where the default configuration file resides""... $ac_c" 1>&6
-echo "configure:3602: checking where the default configuration file resides" >&5
+echo "configure:3639: checking where the default configuration file resides" >&5
 # Check whether --with-cpath or --without-cpath was given.
 if test "${with_cpath+set}" = set; then
   withval="$with_cpath"
@@ -3640,7 +3677,7 @@ EOF
 
 
 echo $ac_n "checking where to put the debugging log if debugging enabled""... $ac_c" 1>&6
-echo "configure:3644: checking where to put the debugging log if debugging enabled" >&5
+echo "configure:3681: checking where to put the debugging log if debugging enabled" >&5
 # Check whether --with-lpath or --without-lpath was given.
 if test "${with_lpath+set}" = set; then
   withval="$with_lpath"
@@ -3686,7 +3723,7 @@ EOF
 unet_maxcon=`ulimit -Hn`
 unet_maxcon=`expr $unet_maxcon - 4`
 echo $ac_n "checking max connections""... $ac_c" 1>&6
-echo "configure:3690: checking max connections" >&5
+echo "configure:3727: checking max connections" >&5
 # Check whether --with-maxcon or --without-maxcon was given.
 if test "${with_maxcon+set}" = set; then
   withval="$with_maxcon"
@@ -3862,6 +3899,9 @@ s%@RMPROG@%$RMPROG%g
 s%@SHPROG@%$SHPROG%g
 s%@OSDEP_C@%$OSDEP_C%g
 s%@ENGINE_C@%$ENGINE_C%g
+s%@subdirs@%$subdirs%g
+s%@RES_C@%$RES_C%g
+s%@ADNS_SUBDIR@%$ADNS_SUBDIR%g
 s%@INSTALL_RULE@%$INSTALL_RULE%g
 s%@SYMLINK@%$SYMLINK%g
 s%@IRCDMODE@%$IRCDMODE%g
@@ -4087,6 +4127,104 @@ chmod +x $CONFIG_STATUS
 rm -fr confdefs* $ac_clean_files
 test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
 
+if test "$no_recursion" != yes; then
+
+  # Remove --cache-file and --srcdir arguments so they do not pile up.
+  ac_sub_configure_args=
+  ac_prev=
+  for ac_arg in $ac_configure_args; do
+    if test -n "$ac_prev"; then
+      ac_prev=
+      continue
+    fi
+    case "$ac_arg" in
+    -cache-file | --cache-file | --cache-fil | --cache-fi \
+    | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+      ac_prev=cache_file ;;
+    -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+    | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+      ;;
+    -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+      ac_prev=srcdir ;;
+    -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+      ;;
+    *) ac_sub_configure_args="$ac_sub_configure_args $ac_arg" ;;
+    esac
+  done
+
+  for ac_config_dir in adns; do
+
+    # Do not complain, so a configure script can configure whichever
+    # parts of a large source tree are present.
+    if test ! -d $srcdir/$ac_config_dir; then
+      continue
+    fi
+
+    echo configuring in $ac_config_dir
+
+    case "$srcdir" in
+    .) ;;
+    *)
+      if test -d ./$ac_config_dir || mkdir ./$ac_config_dir; then :;
+      else
+        { echo "configure: error: can not create `pwd`/$ac_config_dir" 1>&2; exit 1; }
+      fi
+      ;;
+    esac
+
+    ac_popdir=`pwd`
+    cd $ac_config_dir
+
+      # A "../" for each directory in /$ac_config_dir.
+      ac_dots=`echo $ac_config_dir|sed -e 's%^\./%%' -e 's%[^/]$%&/%' -e 's%[^/]*/%../%g'`
+
+    case "$srcdir" in
+    .) # No --srcdir option.  We are building in place.
+      ac_sub_srcdir=$srcdir ;;
+    /*) # Absolute path.
+      ac_sub_srcdir=$srcdir/$ac_config_dir ;;
+    *) # Relative path.
+      ac_sub_srcdir=$ac_dots$srcdir/$ac_config_dir ;;
+    esac
+
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_sub_srcdir/configure; then
+      ac_sub_configure=$ac_sub_srcdir/configure
+    elif test -f $ac_sub_srcdir/configure.in; then
+      ac_sub_configure=$ac_configure
+    else
+      echo "configure: warning: no configuration information is in $ac_config_dir" 1>&2
+      ac_sub_configure=
+    fi
+
+    # The recursion is here.
+    if test -n "$ac_sub_configure"; then
+
+      # Make the cache file name correct relative to the subdirectory.
+      case "$cache_file" in
+      /*) ac_sub_cache_file=$cache_file ;;
+      *) # Relative path.
+        ac_sub_cache_file="$ac_dots$cache_file" ;;
+      esac
+  case "$ac_given_INSTALL" in
+        [/$]*) INSTALL="$ac_given_INSTALL" ;;
+        *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+        esac
+
+      echo "running ${CONFIG_SHELL-/bin/sh} $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_sub_srcdir"
+      # The eval makes quoting arguments work.
+      if eval ${CONFIG_SHELL-/bin/sh} $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_sub_srcdir
+      then :
+      else
+        { echo "configure: error: $ac_sub_configure failed for $ac_config_dir" 1>&2; exit 1; }
+      fi
+    fi
+
+    cd $ac_popdir
+  done
+fi
+
+
 
 echo "
 ircu is now hopefully configured for your system.
@@ -4098,6 +4236,7 @@ ircu is now hopefully configured for your system.
   Debug:               $unet_cv_enable_debug
   Profile:             $unet_cv_enable_profile
   Head-in-sand:        $unet_cv_enable_headinsand
+  ADNS:                $unet_cv_enable_adns
   Owner/mode:          $unet_cv_with_owner.$unet_cv_with_group ($unet_cv_with_mode)
   Chroot:              $unet_cv_with_chroot
   
@@ -4111,3 +4250,4 @@ ircu is now hopefully configured for your system.
   kqueue() engine:     $unet_cv_enable_kqueue
   /dev/poll engine:    $unet_cv_enable_devpoll
 "
+
index 204eb6f21ec89591e03a9e916be171b32d22ed61..7b0fa7dff8a96e27d8e1739b9f99bad5f1b5112d 100644 (file)
@@ -106,11 +106,6 @@ AC_SEARCH_LIBS(crypt, descrypt crypt, ,
 dnl Do all the checks necessary to figure out -lnsl / -lsocket stuff
 AC_LIBRARY_NET
 
-dnl Look for res_mkquery.  Done after AC_LIBRARY_NET in case res_mkquery
-dnl is in one of those libraries somewhere.
-AC_SEARCH_LIBS(res_mkquery, resolv, ,
-[AC_MSG_ERROR([Unable to find library containing res_mkquery()])])
-
 dnl Checks for header files.
 AC_HEADER_STDC
 AC_CHECK_HEADERS(poll.h sys/devpoll.h sys/event.h)
@@ -375,6 +370,34 @@ if test x"$unet_cv_enable_kqueue" != xno; then
     ENGINE_C="engine_kqueue.c $ENGINE_C"
 fi
 
+dnl --disable-adns check...
+AC_MSG_CHECKING([whether to enable the adns resolver engine])
+AC_ARG_ENABLE([adns],
+[  --disable-adns          Disable adns resolver],
+[unet_cv_enable_adns=$enable_adns],
+[AC_CACHE_VAL(unet_cv_enable_adns,
+[unet_cv_enable_adns=yes])])
+
+AC_MSG_RESULT([$unet_cv_enable_adns])
+
+if test x"$unet_cv_enable_adns" != xno; then
+    AC_DEFINE([USE_ADNS], , [Define to enable the adns resolver])
+    AC_CONFIG_SUBDIRS([adns])
+    RES_C="res_adns.c"
+    LIBS="../adns/src/libadns.a $LIBS"
+    ADNS_SUBDIR="adns"
+else
+    RES_C="res_libresolv.c"
+    ADNS_SUBDIR=""
+
+    dnl Look for res_mkquery.  Done after AC_LIBRARY_NET in case res_mkquery
+    dnl is in one of those libraries somewhere.
+    AC_SEARCH_LIBS(res_mkquery, resolv, ,
+    [AC_MSG_ERROR([Unable to find library containing res_mkquery()])])
+fi
+AC_SUBST(RES_C)
+AC_SUBST(ADNS_SUBDIR)
+
 dnl --with-symlink lets us set the name of the symlink; defaults to "ircd"
 AC_MSG_CHECKING([what name to give the symlink])
 AC_ARG_WITH([symlink],
@@ -672,7 +695,7 @@ dnl Finally really generate all output files:
 AC_OUTPUT(Makefile ircd/Makefile doc/Makefile, [echo timestamp > stamp-h])
 
 dnl Report configuration
-echo "
+AC_OUTPUT_COMMANDS([echo "
 ircu is now hopefully configured for your system.
 
   Host system:         $host_os
@@ -682,6 +705,7 @@ ircu is now hopefully configured for your system.
   Debug:               $unet_cv_enable_debug
   Profile:             $unet_cv_enable_profile
   Head-in-sand:        $unet_cv_enable_headinsand
+  ADNS:                $unet_cv_enable_adns
   Owner/mode:          $unet_cv_with_owner.$unet_cv_with_group ($unet_cv_with_mode)
   Chroot:              $unet_cv_with_chroot
   
@@ -694,4 +718,4 @@ ircu is now hopefully configured for your system.
   poll() engine:       $unet_cv_enable_poll
   kqueue() engine:     $unet_cv_enable_kqueue
   /dev/poll engine:    $unet_cv_enable_devpoll
-"
+"])
index c60189dcf10988571db2dba20794181fe3451fc8..c550c0eccb7fbe59af79fbd753ae6e5c3406f749 100644 (file)
@@ -43,6 +43,7 @@ YACC = bison -y
 GREP = grep
 OSDEP_C = @OSDEP_C@
 ENGINE_C = @ENGINE_C@
+RES_C = @RES_C@
 @SET_MAKE@
 
 BINDIR = @bindir@
@@ -57,7 +58,7 @@ RPATH = ${DPATH}/remote.motd
 
 CC = @CC@
 CFLAGS = @CFLAGS@
-CPPFLAGS = -I. -I.. -I${top_srcdir}/include @CPPFLAGS@
+CPPFLAGS = -I. -I.. -I${top_srcdir}/include -I${top_srcdir}/adns/src @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
 LIBS = @LIBS@
 
@@ -78,6 +79,10 @@ ENGINE_SRC = \
        engine_kqueue.c \
        engine_select.c
 
+RES_SRC = \
+       res_adns.c \
+       res_libresolv.c
+
 IRCD_SRC = \
        IPcheck.c \
        channel.c \
@@ -184,7 +189,6 @@ IRCD_SRC = \
        parse.c \
        querycmds.c \
        random.c \
-       res.c \
        s_auth.c \
        s_bsd.c \
        s_conf.c \
@@ -204,7 +208,7 @@ IRCD_SRC = \
        y.tab.c
 
 
-SRC = ${IRCD_SRC} ${OSDEP_C} ${ENGINE_C}
+SRC = ${IRCD_SRC} ${OSDEP_C} ${ENGINE_C} ${RES_C}
 
 OBJS = ${SRC:%.c=%.o}
 
diff --git a/ircd/res.c b/ircd/res.c
deleted file mode 100644 (file)
index d4d85fd..0000000
+++ /dev/null
@@ -1,1803 +0,0 @@
-/*
- * src/res.c (C)opyright 1992 Darren Reed. All rights reserved.
- * This file may not be distributed without the author's permission in any
- * shape or form. The author takes no responsibility for any damage or loss
- * of property which results from the use of this software.
- *
- * $Id$
- *
- * July 1999 - Rewrote a bunch of stuff here. Change hostent builder code,
- *     added callbacks and reference counting of returned hostents.
- *     --Bleep (Thomas Helvey <tomh@inxpress.net>)
- */
-#include "config.h"
-
-#include "res.h"
-#include "client.h"
-#include "ircd.h"
-#include "ircd_alloc.h"
-#include "ircd_events.h"
-#include "ircd_log.h"
-#include "ircd_osdep.h"
-#include "ircd_reply.h"
-#include "ircd_snprintf.h"
-#include "ircd_string.h"
-#include "msg.h"
-#include "numeric.h"
-#include "s_bsd.h"
-#include "s_debug.h"
-#include "s_misc.h"
-#include "send.h"
-#include "struct.h"
-#include "support.h"
-#include "sys.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <regex.h>
-
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-
-#include <limits.h>
-#if (CHAR_BIT != 8)
-#error this code needs to be able to address individual octets 
-#endif
-
-/*
- * Some systems do not define INADDR_NONE (255.255.255.255)
- * INADDR_NONE is actually a valid address, but it should never
- * be returned from any nameserver.
- * NOTE: The bit pattern for INADDR_NONE and INADDR_ANY (0.0.0.0) should be 
- * the same on all hosts so we shouldn't need to use htonl or ntohl to
- * compare or set the values.
- */ 
-#ifndef INADDR_NONE
-#define INADDR_NONE ((unsigned int) 0xffffffff)
-#endif
-
-#define MAXPACKET       1024  /* rfc sez 512 but we expand names so ... */
-#define RES_MAXALIASES  35    /* maximum aliases allowed */
-#define RES_MAXADDRS    35    /* maximum addresses allowed */
-/*
- * OSF1 doesn't have RES_NOALIASES
- */
-#ifndef RES_NOALIASES
-#define RES_NOALIASES 0
-#endif
-
-/*
- * macros used to calulate offsets into fixed query buffer
- */
-#define ALIAS_BLEN  ((RES_MAXALIASES + 1) * sizeof(char*))
-#define ADDRS_BLEN  ((RES_MAXADDRS + 1) * sizeof(struct in_addr*))
-
-#define ADDRS_OFFSET   (ALIAS_BLEN + ADDRS_BLEN)
-#define ADDRS_DLEN     (RES_MAXADDRS * sizeof(struct in_addr))
-#define NAMES_OFFSET   (ADDRS_OFFSET + ADDRS_DLEN)
-#define MAXGETHOSTLEN  (NAMES_OFFSET + MAXPACKET)
-
-#define AR_TTL          600   /* TTL in seconds for dns cache entries */
-
-/*
- * the following values should be prime
- */
-#define ARES_CACSIZE    307
-#define MAXCACHED       281
-
-/*
- * RFC 1104/1105 wasn't very helpful about what these fields
- * should be named, so for now, we'll just name them this way.
- * we probably should look at what named calls them or something.
- */
-#define TYPE_SIZE       2
-#define CLASS_SIZE      2
-#define TTL_SIZE        4
-#define RDLENGTH_SIZE   2
-#define ANSWER_FIXED_SIZE (TYPE_SIZE + CLASS_SIZE + TTL_SIZE + RDLENGTH_SIZE)
-
-/*
- * Building the Hostent
- * The Hostent struct is arranged like this:
- *          +-------------------------------+
- * Hostent: | struct hostent h              |
- *          |-------------------------------|
- *          | char *buf                     |
- *          +-------------------------------+
- *
- * allocated:
- *
- *          +-------------------------------+
- * buf:     | h_aliases pointer array       | Max size: ALIAS_BLEN;
- *          | NULL                          | contains `char *'s
- *          |-------------------------------|
- *          | h_addr_list pointer array     | Max size: ADDRS_BLEN;
- *          | NULL                          | contains `struct in_addr *'s
- *          |-------------------------------|
- *          | h_addr_list addresses         | Max size: ADDRS_DLEN;
- *          |                               | contains `struct in_addr's
- *          |-------------------------------|
- *          | storage for hostname strings  | Max size: ALIAS_DLEN;
- *          +-------------------------------+ contains `char's
- *
- *  For requests the size of the h_aliases, and h_addr_list pointer
- *  array sizes are set to MAXALISES and MAXADDRS respectively, and
- *  buf is a fixed size with enough space to hold the largest expected
- *  reply from a nameserver, see RFC 1034 and RFC 1035.
- *  For cached entries the sizes are dependent on the actual number
- *  of aliases and addresses. If new aliases and addresses are found
- *  for cached entries, the buffer is grown and the new entries are added.
- *  The hostent struct is filled in with the addresses of the entries in
- *  the Hostent buf as follows:
- *  h_name      - contains a pointer to the start of the hostname string area,
- *                or NULL if none is set.  The h_name is followed by the
- *                aliases, in the storage for hostname strings area.
- *  h_aliases   - contains a pointer to the start of h_aliases pointer array.
- *                This array contains pointers to the storage for hostname
- *                strings area and is terminated with a NULL.  The first alias
- *                is stored directly after the h_name.
- *  h_addr_list - contains a pointer to the start of h_addr_list pointer array.
- *                This array contains pointers to in_addr structures in the
- *                h_addr_list addresses area and is terminated with a NULL.
- *
- *  Filling the buffer this way allows for proper alignment of the h_addr_list
- *  addresses.
- *
- *  This arrangement allows us to alias a Hostent struct pointer as a
- *  real struct hostent* without lying. It also allows us to change the
- *  values contained in the cached entries and requests without changing
- *  the actual hostent pointer, which is saved in a client struct and can't
- *  be changed without blowing things up or a lot more fiddling around.
- *  It also allows for defered allocation of the fixed size buffers until
- *  they are really needed.
- *  Nov. 17, 1997 --Bleep
- */
-
-struct Hostent {
-  struct hostent h;      /* the hostent struct we are passing around */
-  char*          buf;    /* buffer for data pointed to from hostent */
-};
-
-struct ResRequest {
-  struct ResRequest* next;
-  int                id;
-  int                sent;          /* number of requests sent */
-  time_t             ttl;
-  char               type;
-  char               retries;       /* retry counter */
-  char               sends;         /* number of sends (>1 means resent) */
-  char               resend;        /* send flag. 0 == dont resend */
-  time_t             sentat;
-  time_t             timeout;
-  struct in_addr     addr;
-  char*              name;
-  struct DNSQuery    query;         /* query callback for this request */
-  struct Hostent     he;
-};
-
-struct CacheEntry {
-  struct CacheEntry* hname_next;
-  struct CacheEntry* hnum_next;
-  struct CacheEntry* list_next;
-  time_t             expireat;
-  time_t             ttl;
-  struct Hostent     he;
-  struct DNSReply    reply;
-};
-
-struct CacheTable {
-  struct CacheEntry* num_list;
-  struct CacheEntry* name_list;
-};
-
-
-int ResolverFileDescriptor    = -1;   /* GLOBAL - used in s_bsd.c */
-
-static struct Socket resSock;          /* Socket describing resolver */
-static struct Timer  resExpireDNS;     /* Timer for DNS expiration */
-static struct Timer  resExpireCache;   /* Timer for cache expiration */
-
-static time_t nextDNSCheck    = 0;
-static time_t nextCacheExpire = 1;
-
-/*
- * Keep a spare file descriptor open. res_init calls fopen to read the
- * resolv.conf file. If ircd is hogging all the file descriptors below 256,
- * on systems with crippled FILE structures this will cause wierd bugs.
- * This is definitely needed for Solaris which uses an unsigned char to
- * hold the file descriptor.  --Dianora
- */ 
-static int                spare_fd = -1;
-
-static int                cachedCount = 0;
-static struct CacheTable  hashtable[ARES_CACSIZE];
-static struct CacheEntry* cacheTop;
-static struct ResRequest* requestListHead;   /* head of resolver request list */
-static struct ResRequest* requestListTail;   /* tail of resolver request list */
-
-
-static void     add_request(struct ResRequest* request);
-static void     rem_request(struct ResRequest* request);
-static struct ResRequest*   make_request(const struct DNSQuery* query);
-static time_t   timeout_query_list(time_t now);
-static time_t   expire_cache(time_t now);
-static void     rem_cache(struct CacheEntry*);
-static void     do_query_name(const struct DNSQuery* query, 
-                              const char* name, 
-                              struct ResRequest* request);
-static void     do_query_number(const struct DNSQuery* query,
-                                const struct in_addr*, 
-                                struct ResRequest* request);
-static void     query_name(const char* name, 
-                           int query_class, 
-                           int query_type, 
-                           struct ResRequest* request);
-static void     resend_query(struct ResRequest* request);
-static struct CacheEntry*  make_cache(struct ResRequest* request);
-static struct CacheEntry*  find_cache_name(const char* name);
-static struct CacheEntry*  find_cache_number(struct ResRequest* request, 
-                                             const char* addr);
-static struct ResRequest*   find_id(int);
-
-static struct cacheinfo {
-  int  ca_adds;
-  int  ca_dels;
-  int  ca_expires;
-  int  ca_lookups;
-  int  ca_na_hits;
-  int  ca_nu_hits;
-  int  ca_updates;
-} cainfo;
-
-static  struct  resinfo {
-  int  re_errors;
-  int  re_nu_look;
-  int  re_na_look;
-  int  re_replies;
-  int  re_requests;
-  int  re_resends;
-  int  re_sent;
-  int  re_timeouts;
-  int  re_shortttl;
-  int  re_unkrep;
-} reinfo;
-
-
-/*
- * From bind 8.3, these aren't declared in earlier versions of bind
- */
-extern u_short  _getshort(const u_char *);
-extern u_int    _getlong(const u_char *);
-/*
- * int
- * res_isourserver(ina)
- *      looks up "ina" in _res.ns_addr_list[]
- * returns:
- *      0  : not found
- *      >0 : found
- * author:
- *      paul vixie, 29may94
- */
-static int
-res_ourserver(const struct __res_state* statp, const struct sockaddr_in* inp) 
-{
-  struct sockaddr_in ina;
-  int ns;
-
-  ina = *inp;
-  for (ns = 0;  ns < statp->nscount;  ns++) {
-    const struct sockaddr_in *srv = &statp->nsaddr_list[ns];
-
-    if (srv->sin_family == ina.sin_family &&
-         srv->sin_port == ina.sin_port &&
-         (srv->sin_addr.s_addr == INADDR_ANY ||
-          srv->sin_addr.s_addr == ina.sin_addr.s_addr))
-             return (1);
-  }
-  return (0);
-}
-
-/* Socket callback for resolver */
-static void res_callback(struct Event* ev)
-{
-  assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
-
-  resolver_read();
-}
-
-/*
- * start_resolver - do everything we need to read the resolv.conf file
- * and initialize the resolver file descriptor if needed
- */
-static void start_resolver(void)
-{
-  Debug((DEBUG_DNS, "Resolver: start_resolver"));
-  /*
-   * close the spare file descriptor so res_init can read resolv.conf
-   * successfully. Needed on Solaris
-   */
-  if (spare_fd > -1)
-    close(spare_fd);
-
-  res_init();      /* res_init always returns 0 */
-  /*
-   * make sure we have a valid file descriptor below 256 so we can
-   * do this again. Needed on Solaris
-   */
-  spare_fd = open("/dev/null",O_RDONLY,0);
-  if ((spare_fd < 0) || (spare_fd > 255)) {
-    char sparemsg[80];
-    ircd_snprintf(0, sparemsg, sizeof(sparemsg), "invalid spare_fd %d",
-                 spare_fd);
-    server_restart(sparemsg);
-  }
-
-  if (!_res.nscount) {
-    _res.nscount = 1;
-    _res.nsaddr_list[0].sin_addr.s_addr = inet_addr("127.0.0.1");
-  }
-  _res.options |= RES_NOALIASES;
-
-  if (ResolverFileDescriptor < 0) {
-    ResolverFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
-    if (-1 == ResolverFileDescriptor) {
-      report_error("Resolver: error creating socket for %s: %s", 
-                   cli_name(&me), errno);
-      return;
-    }
-    if (!os_set_nonblocking(ResolverFileDescriptor))
-      report_error("Resolver: error setting non-blocking for %s: %s", 
-                   cli_name(&me), errno);
-    if (!socket_add(&resSock, res_callback, 0, SS_DATAGRAM,
-                   SOCK_EVENT_READABLE, ResolverFileDescriptor))
-      report_error("Resolver: unable to queue resolver file descriptor for %s",
-                  cli_name(&me), ENFILE);
-  }
-}
-
-/* Call the query timeout function */
-static void expire_DNS_callback(struct Event* ev)
-{
-  time_t next;
-
-  next = timeout_query_list(CurrentTime);
-
-  timer_add(&resExpireDNS, expire_DNS_callback, 0, TT_ABSOLUTE, next);
-}
-
-/* Call the cache expire function */
-static void expire_cache_callback(struct Event* ev)
-{
-  time_t next;
-
-  next = expire_cache(CurrentTime);
-
-  timer_add(&resExpireCache, expire_cache_callback, 0, TT_ABSOLUTE, next);
-}
-
-/*
- * init_resolver - initialize resolver and resolver library
- */
-int init_resolver(void)
-{
-  Debug((DEBUG_DNS, "Resolver: init_resolver"));
-#ifdef  LRAND48
-  srand48(CurrentTime);
-#endif
-  memset(&cainfo,   0, sizeof(cainfo));
-  memset(hashtable, 0, sizeof(hashtable));
-  memset(&reinfo,   0, sizeof(reinfo));
-
-  requestListHead = requestListTail = 0;
-
-  /* initiate the resolver timers */
-  timer_add(timer_init(&resExpireDNS), expire_DNS_callback, 0,
-           TT_RELATIVE, 1);
-  timer_add(timer_init(&resExpireCache), expire_cache_callback, 0,
-           TT_RELATIVE, 1);
-
-  errno = h_errno = 0;
-
-  start_resolver();
-  Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
-         ResolverFileDescriptor, errno, h_errno, 
-         (strerror(errno)) ? strerror(errno) : "Unknown"));
-  return ResolverFileDescriptor;
-}
-
-/*
- * restart_resolver - flush the cache, reread resolv.conf, reopen socket
- */
-void restart_resolver(void)
-{
-  /* flush_cache();  flush the dns cache */
-  start_resolver();
-}
-
-static int validate_hostent(const struct hostent* hp)
-{
-  const char* name;
-  int  i = 0;
-  assert(0 != hp);
-  for (name = hp->h_name; name; name = hp->h_aliases[i++]) {
-    if (!string_is_hostname(name))
-      return 0;
-  }
-  return 1;
-}
-
-/*
- * add_request - place a new request in the request list
- */
-static void add_request(struct ResRequest* request)
-{
-  assert(0 != request);
-  if (!requestListHead)
-    requestListHead = requestListTail = request;
-  else {
-    requestListTail->next = request;
-    requestListTail = request;
-  }
-  request->next = NULL;
-  ++reinfo.re_requests;
-}
-
-/*
- * rem_request - remove a request from the list. 
- * This must also free any memory that has been allocated for 
- * temporary storage of DNS results.
- */
-static void rem_request(struct ResRequest* request)
-{
-  struct ResRequest** current;
-  struct ResRequest*  prev = NULL;
-
-  assert(0 != request);
-  for (current = &requestListHead; *current; ) {
-    if (*current == request) {
-      *current = request->next;
-      if (requestListTail == request)
-        requestListTail = prev;
-      break;
-    } 
-    prev    = *current;
-    current = &(*current)->next;
-  }
-  MyFree(request->he.buf);
-  MyFree(request->name);
-  MyFree(request);
-}
-
-/*
- * make_request - Create a DNS request record for the server.
- */
-static struct ResRequest* make_request(const struct DNSQuery* query)
-{
-  struct ResRequest* request;
-  assert(0 != query);
-  request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
-  memset(request, 0, sizeof(struct ResRequest));
-
-  request->sentat           = CurrentTime;
-  request->retries          = 3;
-  request->resend           = 1;
-  request->timeout          = 5;    /* start at 5 per RFC1123 */
-  request->addr.s_addr      = INADDR_NONE;
-  request->he.h.h_addrtype  = AF_INET;
-  request->he.h.h_length    = sizeof(struct in_addr);
-  request->query.vptr       = query->vptr;
-  request->query.callback   = query->callback;
-
-#if defined(NULL_POINTER_NOT_ZERO)
-  request->next             = NULL;
-  request->he.buf           = NULL;
-  request->he.h.h_name      = NULL;
-  request->he.h.h_aliases   = NULL;
-  request->he.h.h_addr_list = NULL;
-#endif
-  add_request(request);
-  return request;
-}
-
-/*
- * timeout_query_list - Remove queries from the list which have been 
- * there too long without being resolved.
- */
-static time_t timeout_query_list(time_t now)
-{
-  struct ResRequest* request;
-  struct ResRequest* next_request = 0;
-  time_t             next_time    = 0;
-  time_t             timeout      = 0;
-
-  Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
-  for (request = requestListHead; request; request = next_request) {
-    next_request = request->next;
-    timeout = request->sentat + request->timeout;
-    if (timeout < now) {
-      if (--request->retries <= 0) {
-        ++reinfo.re_timeouts;
-        (*request->query.callback)(request->query.vptr, 0);
-        rem_request(request);
-        continue;
-      }
-      else {
-        request->sentat = now;
-        request->timeout += request->timeout;
-        resend_query(request);
-      }
-    }
-    if (!next_time || timeout < next_time) {
-      next_time = timeout;
-    }
-  }
-  return (next_time > now) ? next_time : (now + AR_TTL);
-}
-
-/*
- * expire_cache - removes entries from the cache which are older 
- * than their expiry times. returns the time at which the server 
- * should next poll the cache.
- */
-static time_t expire_cache(time_t now)
-{
-  struct CacheEntry* cp;
-  struct CacheEntry* cp_next;
-  time_t             expire = 0;
-
-  Debug((DEBUG_DNS, "Resolver: expire_cache at %s", myctime(now)));
-  for (cp = cacheTop; cp; cp = cp_next) {
-    cp_next = cp->list_next;
-    if (cp->expireat < now) {
-      ++cainfo.ca_expires;
-      rem_cache(cp);
-    }
-    else if (!expire || expire > cp->expireat)
-      expire = cp->expireat;
-  }
-  return (expire > now) ? expire : (now + AR_TTL);
-}
-
-/*
- * timeout_resolver - check request list and cache for expired entries
- */
-time_t timeout_resolver(time_t now)
-{
-  if (nextDNSCheck < now)
-    nextDNSCheck = timeout_query_list(now);
-  if (nextCacheExpire < now)
-    nextCacheExpire = expire_cache(now);
-  return IRCD_MIN(nextDNSCheck, nextCacheExpire);
-}
-
-
-/*
- * delete_resolver_queries - cleanup outstanding queries 
- * for which there no longer exist clients or conf lines.
- */
-void delete_resolver_queries(const void* vptr)
-{
-  struct ResRequest* request;
-  struct ResRequest* next_request;
-
-  for (request = requestListHead; request; request = next_request) {
-    next_request = request->next;
-    if (vptr == request->query.vptr)
-      rem_request(request);
-  }
-}
-
-/*
- * send_res_msg - sends msg to all nameservers found in the "_res" structure.
- * This should reflect /etc/resolv.conf. We will get responses
- * which arent needed but is easier than checking to see if nameserver
- * isnt present. Returns number of messages successfully sent to 
- * nameservers or -1 if no successful sends.
- */
-static int send_res_msg(const u_char* msg, int len, int rcount)
-{
-  int i;
-  int sent = 0;
-  int max_queries = IRCD_MIN(_res.nscount, rcount);
-
-  assert(0 != msg);
-  /*
-   * RES_PRIMARY option is not implemented
-   * if (_res.options & RES_PRIMARY || 0 == max_queries)
-   */
-  if (0 == max_queries)
-    max_queries = 1;
-
-  Debug((DEBUG_DNS, "Resolver: sendto %d", max_queries));
-
-  for (i = 0; i < max_queries; i++) {
-    if (sendto(ResolverFileDescriptor, msg, len, 0, 
-               (struct sockaddr*) &(_res.nsaddr_list[i]),
-               sizeof(struct sockaddr_in)) == len) {
-      ++reinfo.re_sent;
-      ++sent;
-    }
-    else
-      log_write(LS_RESOLVER, L_ERROR, 0, "Resolver: send failed %m");
-  }
-  return sent;
-}
-
-/*
- * find_id - find a dns request id (id is determined by dn_mkquery)
- */
-static struct ResRequest* find_id(int id)
-{
-  struct ResRequest* request;
-
-  for (request = requestListHead; request; request = request->next) {
-    if (request->id == id)
-      return request;
-  }
-  return NULL;
-}
-
-/*
- * gethost_byname - get host address from name
- */
-struct DNSReply* gethost_byname(const char* name, 
-                               const struct DNSQuery* query)
-{
-  struct CacheEntry* cp;
-  assert(0 != name);
-
-  Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
-  ++reinfo.re_na_look;
-  if ((cp = find_cache_name(name)))
-    return &(cp->reply);
-
-  do_query_name(query, name, NULL);
-  nextDNSCheck = 1;
-  return NULL;
-}
-
-/*
- * gethost_byaddr - get host name from address
- */
-struct DNSReply* gethost_byaddr(const char* addr,
-                                const struct DNSQuery* query)
-{
-  struct CacheEntry *cp;
-
-  assert(0 != addr);
-
-  Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
-
-  ++reinfo.re_nu_look;
-  if ((cp = find_cache_number(NULL, addr)))
-    return &(cp->reply);
-
-  do_query_number(query, (const struct in_addr*) addr, NULL);
-  nextDNSCheck = 1;
-  return NULL;
-}
-
-/*
- * do_query_name - nameserver lookup name
- */
-static void do_query_name(const struct DNSQuery* query, 
-                          const char* name, struct ResRequest* request)
-{
-  char  hname[HOSTLEN + 1];
-  assert(0 != name);
-
-  ircd_strncpy(hname, name, HOSTLEN);
-  hname[HOSTLEN] = '\0';
-
-  if (!request) {
-    request       = make_request(query);
-    request->type = T_A;
-    request->name = (char*) MyMalloc(strlen(hname) + 1);
-    strcpy(request->name, hname);
-  }
-  query_name(hname, C_IN, T_A, request);
-}
-
-/*
- * do_query_number - Use this to do reverse IP# lookups.
- */
-static void do_query_number(const struct DNSQuery* query, 
-                            const struct in_addr* addr,
-                            struct ResRequest* request)
-{
-  char  ipbuf[32];
-  const unsigned char* cp;
-
-  assert(0 != addr);
-  cp = (const unsigned char*) &addr->s_addr;
-  ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
-               (unsigned int)(cp[3]), (unsigned int)(cp[2]),
-               (unsigned int)(cp[1]), (unsigned int)(cp[0]));
-
-  if (!request) {
-    request              = make_request(query);
-    request->type        = T_PTR;
-    request->addr.s_addr = addr->s_addr;
-  }
-  query_name(ipbuf, C_IN, T_PTR, request);
-}
-
-/*
- * query_name - generate a query based on class, type and name.
- */
-static void query_name(const char* name, int query_class,
-                       int type, struct ResRequest* request)
-{
-  char buf[MAXPACKET];
-  int  request_len = 0;
-
-  assert(0 != name);
-  assert(0 != request);
-
-  Debug((DEBUG_DNS, "Resolver: query_name: %s %d %d", name, query_class, type));
-  memset(buf, 0, sizeof(buf));
-  if ((request_len = res_mkquery(QUERY, name, query_class, type, 
-                                 0, 0, 0, (unsigned char*) buf, sizeof(buf))) > 0) {
-    HEADER* header = (HEADER*) buf;
-#ifndef LRAND48
-    int            k = 0;
-    struct timeval tv;
-#endif
-    /*
-     * generate a unique id
-     * NOTE: we don't have to worry about converting this to and from
-     * network byte order, the nameserver does not interpret this value
-     * and returns it unchanged
-     */
-#ifdef LRAND48
-    do {
-      header->id = (header->id + lrand48()) & 0xffff;
-    } while (find_id(header->id));
-#else
-    gettimeofday(&tv, NULL);
-    do {
-      header->id = (header->id + k + tv.tv_usec) & 0xffff;
-      ++k;
-    } while (find_id(header->id));
-#endif /* LRAND48 */
-    request->id = header->id;
-    ++request->sends;
-    Debug((DEBUG_DNS, "Resolver: query_name %d: %s %d %d", request->id, 
-          name, query_class, type));
-    request->sent += send_res_msg((const unsigned char*) buf, request_len, request->sends);
-  }
-}
-
-static void resend_query(struct ResRequest* request)
-{
-  assert(0 != request);
-
-  if (request->resend == 0)
-    return;
-  ++reinfo.re_resends;
-  switch(request->type) {
-  case T_PTR:
-    do_query_number(NULL, &request->addr, request);
-    break;
-  case T_A:
-    do_query_name(NULL, request->name, request);
-    break;
-  default:
-    break;
-  }
-}
-
-/*
- * proc_answer - process name server reply
- * build a hostent struct in the passed request
- */
-static int proc_answer(struct ResRequest* request, HEADER* header,
-                       u_char* buf, u_char* eob)
-{
-  char   hostbuf[HOSTLEN + 1]; /* working buffer */
-  u_char* current;             /* current position in buf */
-  char** alias;                /* alias list */
-  char** addr;                 /* address list */
-  char** base_addr;            /* original pointer to address list */
-  char*  name;                 /* pointer to name string */
-  char*  address;              /* pointer to address */
-  char*  base_address;         /* original pointer to address */
-  char*  endp;                 /* end of our buffer */
-  int    query_class;          /* answer class */
-  int    type;                 /* answer type */
-  int    rd_length;            /* record data length */
-  int    answer_count = 0;     /* answer counter */
-  int    n;                    /* temp count */
-  int    addr_count  = 0;      /* number of addresses in hostent */
-  int    alias_count = 0;      /* number of aliases in hostent */
-  int    t_ptr_seen = 0;       /* Seen a T_PTR in proc_answer? */
-  struct hostent* hp;          /* hostent getting filled */
-
-  assert(0 != request);
-  assert(0 != header);
-  assert(0 != buf);
-  assert(0 != eob);
-  
-  current = buf + sizeof(HEADER);
-  hp = &(request->he.h);
-  /*
-   * lazy allocation of request->he.buf, we don't allocate a buffer
-   * unless there is something to put in it.
-   */
-  if (!request->he.buf) {
-    request->he.buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
-    request->he.buf[MAXGETHOSTLEN] = '\0';
-    /*
-     * array of alias list pointers starts at beginning of buf
-     */
-    hp->h_aliases = (char**) request->he.buf;
-    hp->h_aliases[0] = NULL;
-    /*
-     * array of address list pointers starts after alias list pointers
-     * the actual addresses follow the the address list pointers
-     */ 
-    hp->h_addr_list = (char**)(request->he.buf + ALIAS_BLEN);
-    /*
-     * don't copy the host address to the beginning of h_addr_list
-     */
-    hp->h_addr_list[0] = NULL;
-  }
-  endp = request->he.buf + MAXGETHOSTLEN;
-  /*
-   * find the end of the address list
-   */
-  addr = hp->h_addr_list;
-  while (*addr) {
-    ++addr;
-    ++addr_count;
-  }
-  base_addr = addr;
-  /*
-   * make address point to first available address slot
-   */
-  address = request->he.buf + ADDRS_OFFSET +
-                    (sizeof(struct in_addr) * addr_count);
-  base_address = address;
-
-  /*
-   * find the end of the alias list
-   */
-  alias = hp->h_aliases;
-  while (*alias) {
-    ++alias;
-    ++alias_count;
-  }
-  /*
-   * make name point to first available space in request->buf
-   */
-  if (alias_count > 0) {
-    name = hp->h_aliases[alias_count - 1];
-    name += (strlen(name) + 1);
-  }
-  else if (hp->h_name)
-    name = hp->h_name + strlen(hp->h_name) + 1;
-  else
-    name = request->he.buf + ADDRS_OFFSET + ADDRS_DLEN;
-  /*
-   * skip past queries
-   */ 
-  while (header->qdcount-- > 0) {
-    if ((n = dn_skipname(current, eob)) < 0)
-      break;
-    current += (n + QFIXEDSZ);
-  }
-  /*
-   * process each answer sent to us blech.
-   */
-  while (header->ancount-- > 0 && current < eob && name < endp) {
-    n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
-    if (n <= 0) {
-      /*
-       * no more answers left
-       */
-      return answer_count;
-    }
-    hostbuf[HOSTLEN] = '\0';
-    /* 
-     * With Address arithmetic you have to be very anal
-     * this code was not working on alpha due to that
-     * (spotted by rodder/jailbird/dianora)
-     */
-    current += (size_t) n;
-
-    if (!((current + ANSWER_FIXED_SIZE) < eob))
-      break;
-
-    type = _getshort(current);
-    current += TYPE_SIZE;
-
-    query_class = _getshort(current);
-    current += CLASS_SIZE;
-
-    request->ttl = _getlong(current);
-    current += TTL_SIZE;
-
-    rd_length = _getshort(current);
-    current += RDLENGTH_SIZE;
-
-    /* 
-     * Wait to set request->type until we verify this structure 
-     */
-    switch(type) {
-    case T_A:
-      /*
-       * check for invalid rd_length or too many addresses
-       * ignore T_A relies if looking for a T_PTR
-       */
-      if (t_ptr_seen)
-       return answer_count;
-      if (rd_length != sizeof(struct in_addr))
-        return answer_count;
-      if (++addr_count < RES_MAXADDRS) {
-        if (answer_count == 1)
-          hp->h_addrtype = (query_class == C_IN) ?  AF_INET : AF_UNSPEC;
-
-        memcpy(address, current, sizeof(struct in_addr));
-        *addr++ = address;
-        *addr = 0;
-        address += sizeof(struct in_addr);
-
-        if (!hp->h_name) {
-          strcpy(name, hostbuf);
-          hp->h_name = name;
-          name += strlen(name) + 1;
-        }
-        Debug((DEBUG_DNS, "Resolver: A %s for %s", 
-               ircd_ntoa((char*) hp->h_addr_list[addr_count - 1]), hostbuf));
-      }
-      current += rd_length;
-      ++answer_count;
-      break;
-    case T_PTR:
-      t_ptr_seen = 1;
-      addr_count = 0;
-      addr = base_addr;
-      *addr = 0;
-      address = base_address;
-      n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
-      if (n < 0) {
-        /*
-         * broken message
-         */
-        return 0;
-      }
-      else if (n == 0) {
-        /*
-         * no more answers left
-         */
-        return answer_count;
-      }
-      /*
-       * This comment is based on analysis by Shadowfax, Wohali and johan, 
-       * not me.  (Dianora) I am only commenting it.
-       *
-       * dn_expand is guaranteed to not return more than sizeof(hostbuf)
-       * but do all implementations of dn_expand also guarantee
-       * buffer is terminated with null byte? Lets not take chances.
-       *  -Dianora
-       */
-      hostbuf[HOSTLEN] = '\0';
-      current += (size_t) n;
-
-      Debug((DEBUG_DNS, "Resolver: PTR %s", hostbuf));
-      /*
-       * copy the returned hostname into the host name
-       * ignore duplicate ptr records
-       */
-      if (!hp->h_name) {
-        strcpy(name, hostbuf);
-        hp->h_name = name;
-        name += strlen(name) + 1;
-      }
-      ++answer_count;
-      break;
-    case T_CNAME:
-      Debug((DEBUG_DNS, "Resolver: CNAME %s", hostbuf));
-      if (++alias_count < RES_MAXALIASES) {
-        ircd_strncpy(name, hostbuf, endp - name);
-        *alias++ = name;
-        *alias   = 0;
-        name += strlen(name) + 1;
-      }
-      current += rd_length;
-      ++answer_count;
-      break;
-    default :
-      Debug((DEBUG_DNS,"Resolver: proc_answer type: %d for: %s", type, hostbuf));
-      break;
-    }
-  }
-  return answer_count;
-}
-
-/*
- * resolver_read - read a dns reply from the nameserver and process it.
- * return 0 if nothing was read from the socket, otherwise return 1
- */
-int resolver_read(void)
-{
-  u_char             buf[sizeof(HEADER) + MAXPACKET];
-  HEADER*            header       = 0;
-  struct ResRequest* request      = 0;
-  struct CacheEntry* cp           = 0;
-  unsigned int       rc           = 0;
-  int                answer_count = 0;
-  struct sockaddr_in sin;
-
-  Debug((DEBUG_DNS, "Resolver: read"));
-  if (IO_SUCCESS != os_recvfrom_nonb(ResolverFileDescriptor,
-                                     (char*) buf, sizeof(buf), &rc, &sin)) {
-    return 0;
-  }
-  if (rc < sizeof(HEADER)) {
-    Debug((DEBUG_DNS, "Resolver: short reply %d: %s", rc, 
-           (strerror(errno)) ? strerror(errno) : "Unknown"));
-    return 0;
-  }
-  /*
-   * convert DNS reply reader from Network byte order to CPU byte order.
-   */
-  header = (HEADER*) buf;
-  /* header->id = ntohs(header->id); */
-  header->ancount = ntohs(header->ancount);
-  header->qdcount = ntohs(header->qdcount);
-  header->nscount = ntohs(header->nscount);
-  header->arcount = ntohs(header->arcount);
-  ++reinfo.re_replies;
-  /*
-   * response for an id which we have already received an answer for
-   * just ignore this response.
-   */
-  if (0 == (request = find_id(header->id))) {
-    Debug((DEBUG_DNS, "Resolver: can't find request id: %d", header->id));
-    return 1;
-  }
-  /*
-   * check against possibly fake replies
-   */
-  if (!res_ourserver(&_res, &sin)) {
-    Debug((DEBUG_DNS, "Resolver: fake reply from: %s", (const char*) &sin.sin_addr));
-    ++reinfo.re_unkrep;
-    return 1;
-  }
-
-  if ((header->rcode != NOERROR) || (header->ancount == 0)) {
-    ++reinfo.re_errors;
-    if (SERVFAIL == header->rcode)
-      resend_query(request);
-    else {
-      /*
-       * If a bad error was returned, we stop here and dont send
-       * send any more (no retries granted).
-       * Isomer: Perhaps we should return these error messages back to
-       *         the client?
-       */
-#ifdef DEBUGMODE
-      switch (header->rcode) {
-        case NOERROR:
-          Debug((DEBUG_DNS, "Fatal DNS error: No Error"));
-          break;
-        case FORMERR:
-          Debug((DEBUG_DNS, "Fatal DNS error: Format Error"));
-          break;
-        case SERVFAIL:
-          Debug((DEBUG_DNS, "Fatal DNS error: Server Failure"));
-          break;
-        case NXDOMAIN:
-          Debug((DEBUG_DNS, "DNS error: Non Existant Domain"));
-          break;
-        case NOTIMP:
-          Debug((DEBUG_DNS, "Fatal DNS error: Not Implemented"));
-          break;
-        case REFUSED:
-          Debug((DEBUG_DNS, "Fatal DNS error: Query Refused"));
-          break;
-        default:
-          Debug((DEBUG_DNS, "Unassigned fatal DNS error: %i", header->rcode));
-          break;
-      }
-#endif /* DEBUGMODE */
-      (*request->query.callback)(request->query.vptr, 0);
-      rem_request(request);
-    } 
-    return 1;
-  }
-  /*
-   * If this fails there was an error decoding the received packet, 
-   * try it again and hope it works the next time.
-   */
-  answer_count = proc_answer(request, header, buf, buf + rc);
-  if (answer_count) {
-    if (T_PTR == request->type) {
-      struct DNSReply* reply = 0;
-      if (0 == request->he.h.h_name) {
-        /*
-         * got a PTR response with no name, something bogus is happening
-         * don't bother trying again, the client address doesn't resolve 
-         */
-        (*request->query.callback)(request->query.vptr, reply);
-        rem_request(request); 
-        return 1;
-      }
-      Debug((DEBUG_DNS, "relookup %s <-> %s",
-             request->he.h.h_name, ircd_ntoa((char*) &request->addr)));
-      /*
-       * Lookup the 'authoritive' name that we were given for the
-       * ip#.  By using this call rather than regenerating the
-       * type we automatically gain the use of the cache with no
-       * extra kludges.
-       */
-      reply = gethost_byname(request->he.h.h_name, &request->query);
-      if (reply) {
-        (*request->query.callback)(request->query.vptr, reply);
-      }
-      else {
-        /*
-         * If name wasn't found, a request has been queued and it will
-         * be the last one queued.  This is rather nasty way to keep
-         * a host alias with the query. -avalon
-         */
-        MyFree(requestListTail->he.buf);
-        requestListTail->he.buf = request->he.buf;
-        request->he.buf = 0;
-        memcpy(&requestListTail->he.h, &request->he.h, sizeof(struct hostent));
-      }
-      rem_request(request);
-    }
-    else {
-      /*
-       * got a name and address response, client resolved
-       * XXX - Bug found here by Dianora -
-       * make_cache() occasionally returns a NULL pointer when a
-       * PTR returned a CNAME, cp was not checked before so the
-       * callback was being called with a value of 0x2C != NULL.
-       */
-      struct DNSReply* reply = 0;
-      if (validate_hostent(&request->he.h)) {
-        if ((cp = make_cache(request)))
-          reply = &cp->reply;
-      }
-      (*request->query.callback)(request->query.vptr, reply);
-      rem_request(request);
-    }
-  }
-  else if (!request->sent) {
-    /*
-     * XXX - we got a response for a query we didn't send with a valid id?
-     * this should never happen, bail here and leave the client unresolved
-     */
-    (*request->query.callback)(request->query.vptr, 0);
-    rem_request(request);
-  }
-  return 1;
-}
-
-/*
- * resolver_read_multiple - process up to count reads
- */
-void resolver_read_multiple(int count)
-{
-  int i = 0;
-  for ( ; i < count; ++i) {
-    if (0 == resolver_read())
-      return;
-  }
-}
-
-static size_t calc_hostent_buffer_size(const struct hostent* hp)
-{
-  char** p;
-  size_t count = 0;
-  assert(0 != hp);
-
-  /*
-   * space for name
-   */
-  count += (strlen(hp->h_name) + 1);
-  /*
-   * space for aliases
-   */
-  for (p = hp->h_aliases; *p; ++p)
-    count += (strlen(*p) + 1 + sizeof(char*));
-  /*
-   * space for addresses
-   */
-  for (p = hp->h_addr_list; *p; ++p)
-    count += (hp->h_length + sizeof(char*));
-  /*
-   * space for 2 nulls to terminate h_aliases and h_addr_list 
-   */
-  count += (2 * sizeof(char*));
-  return count;
-}
-
-
-/*
- * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
- * the data we're putting in it.
- */
-static void dup_hostent(struct Hostent* new_hp, struct hostent* hp)
-{
-  char*  p;
-  char** ap;
-  char** pp;
-  int    alias_count = 0;
-  int    addr_count = 0;
-  size_t bytes_needed = 0;
-
-  assert(0 != new_hp);
-  assert(0 != hp);
-
-  /* how much buffer do we need? */
-  bytes_needed += (strlen(hp->h_name) + 1);
-
-  pp = hp->h_aliases;
-  while (*pp) {
-    bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
-    ++alias_count;
-  }
-  pp = hp->h_addr_list;
-  while (*pp++) {
-    bytes_needed += (hp->h_length + sizeof(char*));
-    ++addr_count;
-  }
-  /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
-  bytes_needed += (2 * sizeof(char*));
-
-  /* Allocate memory */
-  new_hp->buf = (char*) MyMalloc(bytes_needed);
-
-  new_hp->h.h_addrtype = hp->h_addrtype;
-  new_hp->h.h_length = hp->h_length;
-
-  /* first write the address list */
-  pp = hp->h_addr_list;
-  ap = new_hp->h.h_addr_list =
-      (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
-  p = (char*)ap + ((addr_count + 1) * sizeof(char*));
-  while (*pp)
-  {
-    *ap++ = p;
-    memcpy(p, *pp++, hp->h_length);
-    p += hp->h_length;
-  }
-  *ap = 0;
-  /* next write the name */
-  new_hp->h.h_name = p;
-  strcpy(p, hp->h_name);
-  p += (strlen(p) + 1);
-
-  /* last write the alias list */
-  pp = hp->h_aliases;
-  ap = new_hp->h.h_aliases = (char**) new_hp->buf;
-  while (*pp) {
-    *ap++ = p;
-    strcpy(p, *pp++);
-    p += (strlen(p) + 1);
-  }
-  *ap = 0;
-}
-
-/*
- * update_hostent - Add records to a Hostent struct in place.
- */
-static void update_hostent(struct Hostent* hp, char** addr, char** alias)
-{
-  char*  p;
-  char** ap;
-  char** pp;
-  int    alias_count = 0;
-  int    addr_count = 0;
-  char*  buf = NULL;
-  size_t bytes_needed = 0;
-
-  if (!hp || !hp->buf)
-    return;
-
-  /* how much buffer do we need? */
-  bytes_needed = strlen(hp->h.h_name) + 1;
-  pp = hp->h.h_aliases;
-  while (*pp) {
-    bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
-    ++alias_count;
-  }
-  if (alias) {
-    pp = alias;
-    while (*pp) {
-      bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
-      ++alias_count;
-    }
-  }
-  pp = hp->h.h_addr_list;
-  while (*pp++) {
-    bytes_needed += (hp->h.h_length + sizeof(char*));
-    ++addr_count;
-  }
-  if (addr) {
-    pp = addr;
-    while (*pp++) {
-      bytes_needed += (hp->h.h_length + sizeof(char*));
-      ++addr_count;
-    }
-  }
-  /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
-  bytes_needed += 2 * sizeof(char*);
-
-  /* Allocate memory */
-  buf = (char*) MyMalloc(bytes_needed);
-  assert(0 != buf);
-
-  /* first write the address list */
-  pp = hp->h.h_addr_list;
-  ap = hp->h.h_addr_list =
-      (char**)(buf + ((alias_count + 1) * sizeof(char*)));
-  p = (char*)ap + ((addr_count + 1) * sizeof(char*));
-  while (*pp) {
-    memcpy(p, *pp++, hp->h.h_length);
-    *ap++ = p;
-    p += hp->h.h_length;
-  }
-  if (addr) {
-    while (*addr) {
-      memcpy(p, *addr++, hp->h.h_length);
-      *ap++ = p;
-      p += hp->h.h_length;
-    }
-  }
-  *ap = 0;
-
-  /* next write the name */
-  strcpy(p, hp->h.h_name);
-  hp->h.h_name = p;
-  p += (strlen(p) + 1);
-
-  /* last write the alias list */
-  pp = hp->h.h_aliases;
-  ap = hp->h.h_aliases = (char**) buf;
-  while (*pp) {
-    strcpy(p, *pp++);
-    *ap++ = p;
-    p += (strlen(p) + 1);
-  }
-  if (alias) {
-    while (*alias) {
-      strcpy(p, *alias++);
-      *ap++ = p;
-      p += (strlen(p) + 1);
-    }
-  }
-  *ap = 0;
-  /* release the old buffer */
-  p = hp->buf;
-  hp->buf = buf;
-  MyFree(p);
-}
-
-/*
- * hash_number - IP address hash function
- */
-static int hash_number(const unsigned char* ip)
-{
-  /* could use loop but slower */
-  unsigned int hashv;
-  const u_char* p = (const u_char*) ip;
-
-  assert(0 != p);
-
-  hashv = *p++;
-  hashv += hashv + *p++;
-  hashv += hashv + *p++;
-  hashv += hashv + *p;
-  hashv %= ARES_CACSIZE;
-  return hashv;
-}
-
-/*
- * hash_name - hostname hash function
- */
-static int hash_name(const char* name)
-{
-  unsigned int hashv = 0;
-  const u_char* p = (const u_char*) name;
-
-  assert(0 != p);
-
-  for (; *p && *p != '.'; ++p)
-    hashv += *p;
-  hashv %= ARES_CACSIZE;
-  return hashv;
-}
-
-/*
- * add_to_cache - Add a new cache item to the queue and hash table.
- */
-static struct CacheEntry* add_to_cache(struct CacheEntry* ocp)
-{
-  int  hashv;
-
-  assert(0 != ocp);
-
-  ocp->list_next = cacheTop;
-  cacheTop = ocp;
-
-  hashv = hash_name(ocp->he.h.h_name);
-
-  ocp->hname_next = hashtable[hashv].name_list;
-  hashtable[hashv].name_list = ocp;
-
-  hashv = hash_number((const unsigned char*) ocp->he.h.h_addr);
-
-  ocp->hnum_next = hashtable[hashv].num_list;
-  hashtable[hashv].num_list = ocp;
-
-  /*
-   * LRU deletion of excessive cache entries.
-   */
-  if (++cachedCount > MAXCACHED) {
-    struct CacheEntry* cp;
-    struct CacheEntry* cp_next;
-    for (cp = ocp->list_next; cp; cp = cp_next) {
-      cp_next = cp->list_next;
-      rem_cache(cp);
-    }
-  }
-  ++cainfo.ca_adds;
-  return ocp;
-}
-
-/*
- * update_list - does not alter the cache structure passed. It is assumed that
- * it already contains the correct expire time, if it is a new entry. Old
- * entries have the expirey time updated.
-*/
-static void update_list(struct ResRequest* request, struct CacheEntry* cachep)
-{
-  struct CacheEntry*  cp = cachep;
-  char*    s;
-  char*    t;
-  int      i;
-  int      j;
-  char**   ap;
-  char*    addrs[RES_MAXADDRS + 1];
-  char*    aliases[RES_MAXALIASES + 1];
-
-  /*
-   * search for the new cache item in the cache list by hostname.
-   * If found, move the entry to the top of the list and return.
-   */
-  ++cainfo.ca_updates;
-
-  if (!request)
-    return;
-  /*
-   * Compare the cache entry against the new record.  Add any
-   * previously missing names for this entry.
-   */
-  *aliases = 0;
-  ap = aliases;
-  for (i = 0, s = request->he.h.h_name; s; s = request->he.h.h_aliases[i++]) {
-    for (j = 0, t = cp->he.h.h_name; t; t = cp->he.h.h_aliases[j++]) {
-      if (0 == ircd_strcmp(t, s))
-        break;
-    }
-    if (!t) {
-      *ap++ = s;
-      *ap = 0;
-    }
-  }
-  /*
-   * Do the same again for IP#'s.
-   */
-  *addrs = 0;
-  ap = addrs;
-  for (i = 0; (s = request->he.h.h_addr_list[i]); i++) {
-    for (j = 0; (t = cp->he.h.h_addr_list[j]); j++) {
-      if (!memcmp(t, s, sizeof(struct in_addr)))
-        break;
-    }
-    if (!t) {
-      *ap++ = s;
-      *ap = 0;
-    }
-  }
-  if (*addrs || *aliases)
-    update_hostent(&cp->he, addrs, aliases);
-}
-
-/*
- * find_cache_name - find name in nameserver cache
- */
-static struct CacheEntry* find_cache_name(const char* name)
-{
-  struct CacheEntry* cp;
-  char*   s;
-  int     hashv;
-  int     i;
-
-  assert(0 != name);
-  hashv = hash_name(name);
-
-  cp = hashtable[hashv].name_list;
-
-  for (; cp; cp = cp->hname_next) {
-    for (i = 0, s = cp->he.h.h_name; s; s = cp->he.h.h_aliases[i++]) {
-      if (0 == ircd_strcmp(s, name)) {
-        ++cainfo.ca_na_hits;
-        return cp;
-      }
-    }
-  }
-
-  for (cp = cacheTop; cp; cp = cp->list_next) {
-    /*
-     * if no aliases or the hash value matches, we've already
-     * done this entry and all possiblilities concerning it.
-     */
-    if (!cp->he.h.h_name || hashv == hash_name(cp->he.h.h_name))
-      continue;
-    for (i = 0, s = cp->he.h.h_aliases[i]; s; s = cp->he.h.h_aliases[++i]) {
-      if (0 == ircd_strcmp(name, s)) {
-        ++cainfo.ca_na_hits;
-        return cp;
-      }
-    }
-  }
-  return NULL;
-}
-
-/*
- * find_cache_number - find a cache entry by ip# and update its expire time
- */
-static struct CacheEntry* find_cache_number(struct ResRequest* request,
-                                            const char* addr)
-{
-  struct CacheEntry* cp;
-  int     hashv;
-  int     i;
-
-  assert(0 != addr);
-  hashv = hash_number((const unsigned char*) addr);
-  cp = hashtable[hashv].num_list;
-
-  for (; cp; cp = cp->hnum_next) {
-    for (i = 0; cp->he.h.h_addr_list[i]; ++i) {
-      if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
-        ++cainfo.ca_nu_hits;
-        return cp;
-      }
-    }
-  }
-  for (cp = cacheTop; cp; cp = cp->list_next) {
-    /*
-     * single address entry...would have been done by hashed
-     * search above...
-     * if the first IP# has the same hashnumber as the IP# we
-     * are looking for, its been done already.
-     */
-    if (!cp->he.h.h_addr_list[1] || 
-        hashv == hash_number((const unsigned char*) cp->he.h.h_addr_list[0]))
-      continue;
-    for (i = 1; cp->he.h.h_addr_list[i]; ++i) {
-      if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
-        ++cainfo.ca_nu_hits;
-        return cp;
-      }
-    }
-  }
-  return NULL;
-}
-
-static struct CacheEntry* make_cache(struct ResRequest* request)
-{
-  struct CacheEntry* cp;
-  int     i;
-  struct hostent* hp;
-  assert(0 != request);
-
-  hp = &request->he.h;
-  /*
-   * shouldn't happen but it just might...
-   */
-  assert(0 != hp->h_name);
-/*    assert(0 != hp->h_addr_list[0]); */
-  if (!hp->h_name || !hp->h_addr_list[0])
-    return NULL;
-  /*
-   * Make cache entry.  First check to see if the cache already exists
-   * and if so, return a pointer to it.
-   */
-  for (i = 0; hp->h_addr_list[i]; ++i) {
-    if ((cp = find_cache_number(request, hp->h_addr_list[i]))) {
-      update_list(request, cp);
-      return cp;
-    }
-  }
-  /*
-   * a matching entry wasnt found in the cache so go and make one up.
-   */ 
-  cp = (struct CacheEntry*) MyMalloc(sizeof(struct CacheEntry));
-  assert(0 != cp);
-
-  memset(cp, 0, sizeof(struct CacheEntry));
-  dup_hostent(&cp->he, hp);
-  cp->reply.hp = &cp->he.h;
-  /*
-   * hmmm... we could time out the cache after 10 minutes regardless
-   * would that be reasonable since we don't save the reply?
-   */ 
-  if (request->ttl < AR_TTL) {
-    ++reinfo.re_shortttl;
-    cp->ttl = AR_TTL;
-  }
-  else
-    cp->ttl = request->ttl;
-  cp->expireat = CurrentTime + cp->ttl;
-  return add_to_cache(cp);
-}
-
-/*
- * rem_cache - delete a cache entry from the cache structures 
- * and lists and return all memory used for the cache back to the memory pool.
- */
-static void rem_cache(struct CacheEntry* ocp)
-{
-  struct CacheEntry** cp;
-  int                 hashv;
-  struct hostent*     hp;
-  assert(0 != ocp);
-
-
-  if (0 < ocp->reply.ref_count) {
-    if (ocp->expireat < CurrentTime) {
-      ocp->expireat = CurrentTime + AR_TTL;
-      Debug((DEBUG_DNS, "Resolver: referenced cache entry not removed for: %s",
-            ocp->he.h.h_name));
-    }
-    return;
-  }
-  /*
-   * remove cache entry from linked list
-   */
-  for (cp = &cacheTop; *cp; cp = &((*cp)->list_next)) {
-    if (*cp == ocp) {
-      *cp = ocp->list_next;
-      break;
-    }
-  }
-  hp = &ocp->he.h;
-  /*
-   * remove cache entry from hashed name list
-   */
-  assert(0 != hp->h_name);
-  hashv = hash_name(hp->h_name);
-
-  for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next)) {
-    if (*cp == ocp) {
-      *cp = ocp->hname_next;
-      break;
-    }
-  }
-  /*
-   * remove cache entry from hashed number list
-   */
-  hashv = hash_number((const unsigned char*) hp->h_addr);
-  assert(-1 < hashv);
-
-  for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next)) {
-    if (*cp == ocp) {
-      *cp = ocp->hnum_next;
-      break;
-    }
-  }
-  /*
-   * free memory used to hold the various host names and the array
-   * of alias pointers.
-   */
-  MyFree(ocp->he.buf);
-  MyFree(ocp);
-  --cachedCount;
-  ++cainfo.ca_dels;
-}
-
-void flush_resolver_cache(void)
-{
-  /*
-   * stubbed - iterate cache and remove everything that isn't referenced
-   */
-}
-
-/*
- * m_dns - dns status query
- */
-int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
-{
-#if !defined(NDEBUG)
-  struct CacheEntry* cp;
-  int     i;
-  struct hostent* hp;
-
-  if (parv[1] && *parv[1] == 'l') {
-    for(cp = cacheTop; cp; cp = cp->list_next) {
-      hp = &cp->he.h;
-      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Ex %d ttl %d host %s(%s)",
-                   sptr, cp->expireat - CurrentTime, cp->ttl,
-                   hp->h_name, ircd_ntoa(hp->h_addr));
-      for (i = 0; hp->h_aliases[i]; i++)
-        sendcmdto_one(&me, CMD_NOTICE, sptr, "%C : %s = %s (CN)", sptr,
-                     hp->h_name, hp->h_aliases[i]);
-      for (i = 1; hp->h_addr_list[i]; i++)
-        sendcmdto_one(&me, CMD_NOTICE, sptr, "%C : %s = %s (IP)", sptr,
-                     hp->h_name, ircd_ntoa(hp->h_addr_list[i]));
-    }
-    return 0;
-  }
-  if (parv[1] && *parv[1] == 'd') {
-    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :ResolverFileDescriptor = %d", 
-                 sptr, ResolverFileDescriptor);
-    return 0;
-  }
-  sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ca %d Cd %d Ce %d Cl %d Ch %d:%d "
-               "Cu %d", sptr,
-               cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires,
-               cainfo.ca_lookups, cainfo.ca_na_hits, cainfo.ca_nu_hits, 
-               cainfo.ca_updates);
-  
-  sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Re %d Rl %d/%d Rp %d Rq %d",
-               sptr, reinfo.re_errors, reinfo.re_nu_look,
-               reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
-  sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr,
-               reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
-               reinfo.re_resends, reinfo.re_timeouts);
-#endif
-  return 0;
-}
-
-size_t cres_mem(struct Client* sptr)
-{
-  struct CacheEntry* entry;
-  struct ResRequest* request;
-  size_t cache_mem     = 0;
-  size_t request_mem   = 0;
-  int    cache_count   = 0;
-  int    request_count = 0;
-
-  for (entry = cacheTop; entry; entry = entry->list_next) {
-    cache_mem += sizeof(struct CacheEntry);
-    cache_mem += calc_hostent_buffer_size(&entry->he.h); 
-    ++cache_count;
-  }
-  for (request = requestListHead; request; request = request->next) {
-    request_mem += sizeof(struct ResRequest);
-    if (request->name)
-      request_mem += strlen(request->name) + 1; 
-    if (request->he.buf)
-      request_mem += MAXGETHOSTLEN + 1;
-    ++request_count;
-  }
-
-  if (cachedCount != cache_count) {
-    send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
-              ":Resolver: cache count mismatch: %d != %d", cachedCount,
-              cache_count);
-    assert(cachedCount == cache_count);
-  }
-  send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
-            ":Resolver: cache %d(%d) requests %d(%d)", cache_count,
-            cache_mem, request_count, request_mem);
-  return cache_mem + request_mem;
-}
-
diff --git a/ircd/res_adns.c b/ircd/res_adns.c
new file mode 100644 (file)
index 0000000..590d5ef
--- /dev/null
@@ -0,0 +1,1430 @@
+/*
+ * src/res.c (C)opyright 1992 Darren Reed. All rights reserved.
+ * This file may not be distributed without the author's permission in any
+ * shape or form. The author takes no responsibility for any damage or loss
+ * of property which results from the use of this software.
+ *
+ * $Id$
+ *
+ * July 1999 - Rewrote a bunch of stuff here. Change hostent builder code,
+ *     added callbacks and reference counting of returned hostents.
+ *     --Bleep (Thomas Helvey <tomh@inxpress.net>)
+ */
+#include "config.h"
+
+#include "adns.h"
+#include "res.h"
+#include "client.h"
+#include "ircd.h"
+#include "ircd_alloc.h"
+#include "ircd_events.h"
+#include "ircd_log.h"
+#include "ircd_osdep.h"
+#include "ircd_reply.h"
+#include "ircd_snprintf.h"
+#include "ircd_string.h"
+#include "msg.h"
+#include "numeric.h"
+#include "s_bsd.h"
+#include "s_debug.h"
+#include "s_misc.h"
+#include "send.h"
+#include "struct.h"
+#include "support.h"
+#include "sys.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <regex.h>
+
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <limits.h>
+#if (CHAR_BIT != 8)
+#error this code needs to be able to address individual octets 
+#endif
+
+/*
+ * Some systems do not define INADDR_NONE (255.255.255.255)
+ * INADDR_NONE is actually a valid address, but it should never
+ * be returned from any nameserver.
+ * NOTE: The bit pattern for INADDR_NONE and INADDR_ANY (0.0.0.0) should be 
+ * the same on all hosts so we shouldn't need to use htonl or ntohl to
+ * compare or set the values.
+ */ 
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned int) 0xffffffff)
+#endif
+
+#define MAXPACKET       1024  /* rfc sez 512 but we expand names so ... */
+#define RES_MAXALIASES  35    /* maximum aliases allowed */
+#define RES_MAXADDRS    35    /* maximum addresses allowed */
+/*
+ * OSF1 doesn't have RES_NOALIASES
+ */
+#ifndef RES_NOALIASES
+#define RES_NOALIASES 0
+#endif
+
+/*
+ * macros used to calulate offsets into fixed query buffer
+ */
+#define ALIAS_BLEN  ((RES_MAXALIASES + 1) * sizeof(char*))
+#define ADDRS_BLEN  ((RES_MAXADDRS + 1) * sizeof(struct in_addr*))
+
+#define ADDRS_OFFSET   (ALIAS_BLEN + ADDRS_BLEN)
+#define ADDRS_DLEN     (RES_MAXADDRS * sizeof(struct in_addr))
+#define NAMES_OFFSET   (ADDRS_OFFSET + ADDRS_DLEN)
+#define MAXGETHOSTLEN  (NAMES_OFFSET + MAXPACKET)
+
+#define AR_TTL          600   /* TTL in seconds for dns cache entries */
+
+/*
+ * the following values should be prime
+ */
+#define ARES_CACSIZE    307
+#define MAXCACHED       17
+//#define MAXCACHED       281
+
+/*
+ * Building the Hostent
+ * The Hostent struct is arranged like this:
+ *          +-------------------------------+
+ * Hostent: | struct hostent h              |
+ *          |-------------------------------|
+ *          | char *buf                     |
+ *          +-------------------------------+
+ *
+ * allocated:
+ *
+ *          +-------------------------------+
+ * buf:     | h_aliases pointer array       | Max size: ALIAS_BLEN;
+ *          | NULL                          | contains `char *'s
+ *          |-------------------------------|
+ *          | h_addr_list pointer array     | Max size: ADDRS_BLEN;
+ *          | NULL                          | contains `struct in_addr *'s
+ *          |-------------------------------|
+ *          | h_addr_list addresses         | Max size: ADDRS_DLEN;
+ *          |                               | contains `struct in_addr's
+ *          |-------------------------------|
+ *          | storage for hostname strings  | Max size: ALIAS_DLEN;
+ *          +-------------------------------+ contains `char's
+ *
+ *  For requests the size of the h_aliases, and h_addr_list pointer
+ *  array sizes are set to MAXALISES and MAXADDRS respectively, and
+ *  buf is a fixed size with enough space to hold the largest expected
+ *  reply from a nameserver, see RFC 1034 and RFC 1035.
+ *  For cached entries the sizes are dependent on the actual number
+ *  of aliases and addresses. If new aliases and addresses are found
+ *  for cached entries, the buffer is grown and the new entries are added.
+ *  The hostent struct is filled in with the addresses of the entries in
+ *  the Hostent buf as follows:
+ *  h_name      - contains a pointer to the start of the hostname string area,
+ *                or NULL if none is set.  The h_name is followed by the
+ *                aliases, in the storage for hostname strings area.
+ *  h_aliases   - contains a pointer to the start of h_aliases pointer array.
+ *                This array contains pointers to the storage for hostname
+ *                strings area and is terminated with a NULL.  The first alias
+ *                is stored directly after the h_name.
+ *  h_addr_list - contains a pointer to the start of h_addr_list pointer array.
+ *                This array contains pointers to in_addr structures in the
+ *                h_addr_list addresses area and is terminated with a NULL.
+ *
+ *  Filling the buffer this way allows for proper alignment of the h_addr_list
+ *  addresses.
+ *
+ *  This arrangement allows us to alias a Hostent struct pointer as a
+ *  real struct hostent* without lying. It also allows us to change the
+ *  values contained in the cached entries and requests without changing
+ *  the actual hostent pointer, which is saved in a client struct and can't
+ *  be changed without blowing things up or a lot more fiddling around.
+ *  It also allows for defered allocation of the fixed size buffers until
+ *  they are really needed.
+ *  Nov. 17, 1997 --Bleep
+ */
+
+struct Hostent {
+  struct hostent h;      /* the hostent struct we are passing around */
+  char*          buf;    /* buffer for data pointed to from hostent */
+};
+
+struct ResRequest {
+  struct ResRequest* next;
+
+  adns_rrtype        type;
+  adns_query         aq;
+
+  struct in_addr     addr;
+  char*              name;
+  struct DNSQuery    query;         /* query callback for this request */
+  struct Hostent     he;
+};
+
+struct CacheEntry {
+  struct CacheEntry* hname_next;
+  struct CacheEntry* hnum_next;
+  struct CacheEntry* list_next;
+  time_t             expireat;
+  time_t             ttl;
+  struct Hostent     he;
+  struct DNSReply    reply;
+};
+
+struct CacheTable {
+  struct CacheEntry* num_list;
+  struct CacheEntry* name_list;
+};
+
+
+int ResolverFileDescriptor    = -1;   /* GLOBAL - used in s_bsd.c */
+
+static struct Socket resSock;          /* Socket describing resolver */
+static struct Timer  resExpireDNS;     /* Timer for DNS expiration */
+static struct Timer  resExpireCache;   /* Timer for cache expiration */
+static adns_state adns = NULL;
+
+/*
+ * Keep a spare file descriptor open. res_init calls fopen to read the
+ * resolv.conf file. If ircd is hogging all the file descriptors below 256,
+ * on systems with crippled FILE structures this will cause wierd bugs.
+ * This is definitely needed for Solaris which uses an unsigned char to
+ * hold the file descriptor.  --Dianora
+ */ 
+static int                spare_fd = -1;
+
+static int                cachedCount = 0;
+static struct CacheTable  hashtable[ARES_CACSIZE];
+static struct CacheEntry* cacheTop;
+static struct ResRequest* requestListHead;   /* head of resolver request list */
+static struct ResRequest* requestListTail;   /* tail of resolver request list */
+
+
+static void     add_request(struct ResRequest* request);
+static void     rem_request(struct ResRequest* request);
+static struct ResRequest*   make_request(const struct DNSQuery* query);
+static time_t   timeout_query_list(time_t now);
+static time_t   expire_cache(time_t now);
+static void     rem_cache(struct CacheEntry*);
+static void     do_query_name(const struct DNSQuery* query, 
+                              const char* name, 
+                              struct ResRequest* request);
+static void     do_query_number(const struct DNSQuery* query,
+                                const struct in_addr*, 
+                                struct ResRequest* request);
+static struct CacheEntry*  make_cache(struct ResRequest* request);
+static struct CacheEntry*  find_cache_name(const char* name);
+static struct CacheEntry*  find_cache_number(struct ResRequest* request, 
+                                             const char* addr);
+static void res_adns_callback(adns_state state, adns_query q, void *context);
+
+static struct cacheinfo {
+  int  ca_adds;
+  int  ca_dels;
+  int  ca_expires;
+  int  ca_lookups;
+  int  ca_na_hits;
+  int  ca_nu_hits;
+  int  ca_updates;
+} cainfo;
+
+static  struct  resinfo {
+  int  re_errors;
+  int  re_nu_look;
+  int  re_na_look;
+  int  re_replies;
+  int  re_requests;
+  int  re_resends;
+  int  re_sent;
+  int  re_timeouts;
+  int  re_shortttl;
+  int  re_unkrep;
+} reinfo;
+
+
+/*
+ * int
+ * res_isourserver(ina)
+ *      looks up "ina" in _res.ns_addr_list[]
+ * returns:
+ *      0  : not found
+ *      >0 : found
+ * author:
+ *      paul vixie, 29may94
+ */
+static int
+res_ourserver(const struct __res_state* statp, const struct sockaddr_in* inp) 
+{
+  struct sockaddr_in ina;
+  int ns;
+
+  ina = *inp;
+  for (ns = 0;  ns < statp->nscount;  ns++) {
+    const struct sockaddr_in *srv = &statp->nsaddr_list[ns];
+
+    if (srv->sin_family == ina.sin_family &&
+         srv->sin_port == ina.sin_port &&
+         (srv->sin_addr.s_addr == INADDR_ANY ||
+          srv->sin_addr.s_addr == ina.sin_addr.s_addr))
+             return (1);
+  }
+  return (0);
+}
+
+/* Socket callback for resolver */
+static void res_socket_callback(struct Event* ev)
+{
+  struct timeval tv;
+
+  assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
+
+  tv.tv_sec = CurrentTime;
+  tv.tv_usec = 0;
+  adns_processreadable(adns, ResolverFileDescriptor, &tv);
+}
+
+/*
+ * start_resolver - do everything we need to read the resolv.conf file
+ * and initialize the resolver file descriptor if needed
+ */
+static void start_resolver(void)
+{
+  int res;
+
+  Debug((DEBUG_DNS, "Resolver: start_resolver"));
+  /*
+   * close the spare file descriptor so res_init can read resolv.conf
+   * successfully. Needed on Solaris
+   */
+  if (spare_fd > -1)
+    close(spare_fd);
+
+  /*
+   * make sure we have a valid file descriptor below 256 so we can
+   * do this again. Needed on Solaris
+   */
+  spare_fd = open("/dev/null",O_RDONLY,0);
+  if ((spare_fd < 0) || (spare_fd > 255)) {
+    char sparemsg[80];
+    ircd_snprintf(0, sparemsg, sizeof(sparemsg), "invalid spare_fd %d",
+                 spare_fd);
+    server_restart(sparemsg);
+  }
+
+  if (adns)
+    return;
+
+  if ((res = adns_init(&adns, adns_if_debug /*| adns_if_noautosys*/, NULL))) {
+    report_error("Resolver: error initializing adns for %s: %s", cli_name(&me), res);
+    errno = res;
+    return;
+  }
+  errno = 0;
+  
+  ResolverFileDescriptor = adns_get_fd(adns);
+
+  if (!socket_add(&resSock, res_socket_callback, 0, SS_DATAGRAM,
+                 SOCK_EVENT_READABLE, ResolverFileDescriptor))
+    report_error("Resolver: unable to queue resolver file descriptor for %s",
+                cli_name(&me), ENFILE);
+}
+
+/* Call the query timeout function */
+static void expire_DNS_callback(struct Event* ev)
+{
+  time_t next;
+
+  next = timeout_query_list(CurrentTime);
+
+  timer_add(&resExpireDNS, expire_DNS_callback, 0, TT_ABSOLUTE, next);
+}
+
+/* Call the cache expire function */
+static void expire_cache_callback(struct Event* ev)
+{
+  time_t next;
+
+  next = expire_cache(CurrentTime);
+
+  timer_add(&resExpireCache, expire_cache_callback, 0, TT_ABSOLUTE, next);
+}
+
+/*
+ * init_resolver - initialize resolver and resolver library
+ */
+int init_resolver(void)
+{
+  Debug((DEBUG_DNS, "Resolver: init_resolver"));
+#ifdef  LRAND48
+  srand48(CurrentTime);
+#endif
+  memset(&cainfo,   0, sizeof(cainfo));
+  memset(hashtable, 0, sizeof(hashtable));
+  memset(&reinfo,   0, sizeof(reinfo));
+
+  requestListHead = requestListTail = 0;
+
+  /* initiate the resolver timers */
+  timer_add(timer_init(&resExpireDNS), expire_DNS_callback, 0,
+           TT_RELATIVE, 1);
+  timer_add(timer_init(&resExpireCache), expire_cache_callback, 0,
+           TT_RELATIVE, 1);
+
+  errno = h_errno = 0;
+
+  start_resolver();
+  Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
+         ResolverFileDescriptor, errno, h_errno, 
+         (strerror(errno)) ? strerror(errno) : "Unknown"));
+  return ResolverFileDescriptor;
+}
+
+/*
+ * restart_resolver - flush the cache, reread resolv.conf, reopen socket
+ */
+void restart_resolver(void)
+{
+  /* flush_cache();  flush the dns cache */
+  start_resolver();
+}
+
+/*
+ * add_request - place a new request in the request list
+ */
+static void add_request(struct ResRequest* request)
+{
+  assert(0 != request);
+  if (!requestListHead)
+    requestListHead = requestListTail = request;
+  else {
+    requestListTail->next = request;
+    requestListTail = request;
+  }
+  request->next = NULL;
+  ++reinfo.re_requests;
+}
+
+/*
+ * rem_request - remove a request from the list. 
+ * This must also free any memory that has been allocated for 
+ * temporary storage of DNS results.
+ */
+static void rem_request(struct ResRequest* request)
+{
+  struct ResRequest** current;
+  struct ResRequest*  prev = NULL;
+
+  assert(0 != request);
+  for (current = &requestListHead; *current; ) {
+    if (*current == request) {
+      *current = request->next;
+      if (requestListTail == request)
+        requestListTail = prev;
+      break;
+    } 
+    prev    = *current;
+    current = &(*current)->next;
+  }
+  if (request->aq)
+    adns_cancel(request->aq);
+  MyFree(request->he.buf);
+  MyFree(request->name);
+  MyFree(request);
+}
+
+/*
+ * make_request - Create a DNS request record for the server.
+ */
+static struct ResRequest* make_request(const struct DNSQuery* query)
+{
+  struct ResRequest* request;
+  assert(0 != query);
+  request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
+  memset(request, 0, sizeof(struct ResRequest));
+
+  request->addr.s_addr      = INADDR_NONE;
+  request->he.h.h_addrtype  = AF_INET;
+  request->he.h.h_length    = sizeof(struct in_addr);
+  request->query.vptr       = query->vptr;
+  request->query.callback   = query->callback;
+
+#if defined(NULL_POINTER_NOT_ZERO)
+  request->next             = NULL;
+  request->he.buf           = NULL;
+  request->he.h.h_name      = NULL;
+  request->he.h.h_aliases   = NULL;
+  request->he.h.h_addr_list = NULL;
+#endif
+  add_request(request);
+  return request;
+}
+
+/*
+ * timeout_query_list - Remove queries from the list which have been 
+ * there too long without being resolved.
+ */
+static time_t timeout_query_list(time_t now)
+{
+  struct timeval tv, tv_buf, *tv_mod = NULL;
+  time_t next;
+  
+  Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
+
+  tv.tv_sec = now;
+  tv.tv_usec = 0;
+  adns_processtimeouts(adns, &tv);
+  adns_firsttimeout(adns, &tv_mod, &tv_buf, tv);
+  next = tv_mod ? tv_mod->tv_sec : AR_TTL;
+  if (!next)
+    next = 1;
+
+  Debug((DEBUG_DNS, "Resolver: next timeout_query_list in %d seconds", next));
+  return now + next;
+}
+
+/*
+ * expire_cache - removes entries from the cache which are older 
+ * than their expiry times. returns the time at which the server 
+ * should next poll the cache.
+ */
+static time_t expire_cache(time_t now)
+{
+  struct CacheEntry* cp;
+  struct CacheEntry* cp_next;
+  time_t             expire = 0;
+
+  Debug((DEBUG_DNS, "Resolver: expire_cache at %s", myctime(now)));
+  for (cp = cacheTop; cp; cp = cp_next) {
+    cp_next = cp->list_next;
+    if (cp->expireat < now) {
+      ++cainfo.ca_expires;
+      rem_cache(cp);
+    }
+    else if (!expire || expire > cp->expireat)
+      expire = cp->expireat;
+  }
+  return (expire > now) ? expire : (now + AR_TTL);
+}
+
+/*
+ * delete_resolver_queries - cleanup outstanding queries 
+ * for which there no longer exist clients or conf lines.
+ */
+void delete_resolver_queries(const void* vptr)
+{
+  struct ResRequest* request;
+  struct ResRequest* next_request;
+
+  for (request = requestListHead; request; request = next_request) {
+    next_request = request->next;
+    if (vptr == request->query.vptr)
+      rem_request(request);
+  }
+}
+
+/*
+ * gethost_byname - get host address from name
+ */
+struct DNSReply* gethost_byname(const char* name, 
+                               const struct DNSQuery* query)
+{
+  struct CacheEntry* cp;
+  assert(0 != name);
+
+  Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
+  ++reinfo.re_na_look;
+  if ((cp = find_cache_name(name)))
+    return &(cp->reply);
+
+  do_query_name(query, name, NULL);
+  return NULL;
+}
+
+/*
+ * gethost_byaddr - get host name from address
+ */
+struct DNSReply* gethost_byaddr(const char* addr,
+                                const struct DNSQuery* query)
+{
+  struct CacheEntry *cp;
+
+  assert(0 != addr);
+
+  Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
+
+  ++reinfo.re_nu_look;
+  if ((cp = find_cache_number(NULL, addr)))
+    return &(cp->reply);
+
+  do_query_number(query, (const struct in_addr*) addr, NULL);
+  return NULL;
+}
+
+/*
+ * do_query_name - nameserver lookup name
+ */
+static void do_query_name(const struct DNSQuery* query, 
+                          const char* name, struct ResRequest* request)
+{
+  char  hname[HOSTLEN + 1];
+  int  res;
+  assert(0 != name);
+
+  ircd_strncpy(hname, name, HOSTLEN);
+  hname[HOSTLEN] = '\0';
+
+  if (!request) {
+    request       = make_request(query);
+    request->type = adns_r_a;
+    request->name = (char*) MyMalloc(strlen(hname) + 1);
+    strcpy(request->name, hname);
+  }
+  res = adns_submit_callback(adns, hname, adns_r_a, adns_qf_owner, request, &request->aq, res_adns_callback);
+  assert(!res);
+  timer_chg(&resExpireDNS, TT_RELATIVE, 1);
+}
+
+/*
+ * do_query_number - Use this to do reverse IP# lookups.
+ */
+static void do_query_number(const struct DNSQuery* query, 
+                            const struct in_addr* addr,
+                            struct ResRequest* request)
+{
+  char  ipbuf[32];
+  const unsigned char* cp;
+  int  res;
+
+  assert(0 != addr);
+  cp = (const unsigned char*) &addr->s_addr;
+  ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
+               (unsigned int)(cp[3]), (unsigned int)(cp[2]),
+               (unsigned int)(cp[1]), (unsigned int)(cp[0]));
+
+  if (!request) {
+    request              = make_request(query);
+    request->type        = adns_r_ptr;
+    request->addr.s_addr = addr->s_addr;
+  }
+  res = adns_submit_callback(adns, ipbuf, adns_r_ptr, adns_qf_owner, request, &request->aq, res_adns_callback);
+  assert(!res);
+  timer_chg(&resExpireDNS, TT_RELATIVE, 1);
+}
+
+/* Returns true if this is a valid name */
+static int validate_name(char *name)
+{
+  while (*name) {
+    if ((*name<'A' || *name>'Z') 
+     && (*name<'a' || *name>'z') 
+     && (*name<'0' || *name>'9')
+     && (*name!='-')) {
+      return 0;
+    }
+    name++;
+  }
+  return 1;
+}
+
+static void res_adns_callback(adns_state state, adns_query q, void *context)
+{
+  struct ResRequest *request = (struct ResRequest *) context;
+  adns_answer *answer = NULL;
+  int res, k;
+  struct hostent* hp;          /* hostent getting filled */
+  char** alias;                /* alias list */
+  char** addr;                 /* address list */
+  char** base_addr;            /* original pointer to address list */
+  char*  name;                 /* pointer to name string */
+  char*  address;              /* pointer to address */
+  char*  base_address;         /* original pointer to address */
+  char*  endp;                 /* end of our buffer */
+  int    addr_count  = 0;      /* number of addresses in hostent */
+  int    alias_count = 0;      /* number of aliases in hostent */
+  struct CacheEntry* cp           = 0;
+  
+  assert(request);
+  assert(q);
+  assert(request->aq == q);
+  res = adns_check(adns, &q, &answer, NULL);
+  request->aq = NULL;
+
+  if (res) {
+    /* adns_check returned an error, bail */
+    Debug((DEBUG_DNS, "Resolver: adns_check result %d", res));
+
+    (*request->query.callback)(request->query.vptr, 0);
+    rem_request(request);
+    return;
+  }
+
+  Debug((DEBUG_DNS, "Resolver: adns_check status %d nrrs %d", answer->status, answer->nrrs));
+  
+  /* No error, we have a valid answer structure */
+  if (answer->status != adns_s_ok || !answer->nrrs) {
+    /* Status is not 'ok', or there were no RRs found */
+    ++reinfo.re_errors;
+    (*request->query.callback)(request->query.vptr, 0);
+  } else {
+    ++reinfo.re_replies;
+    hp = &(request->he.h);    
+    if (!request->he.buf) {
+      request->he.buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
+      request->he.buf[MAXGETHOSTLEN] = '\0';
+      /*
+       * array of alias list pointers starts at beginning of buf
+       */
+      hp->h_aliases = (char**) request->he.buf;
+      hp->h_aliases[0] = NULL;
+      /*
+       * array of address list pointers starts after alias list pointers
+       * the actual addresses follow the the address list pointers
+       */ 
+      hp->h_addr_list = (char**)(request->he.buf + ALIAS_BLEN);
+      /*
+       * don't copy the host address to the beginning of h_addr_list
+       */
+      hp->h_addr_list[0] = NULL;
+    }
+
+    hp->h_addrtype = AF_INET;
+
+    endp = request->he.buf + MAXGETHOSTLEN;
+    /*
+     * find the end of the address list
+     */
+    addr = hp->h_addr_list;
+    while (*addr) {
+      ++addr;
+      ++addr_count;
+    }
+    base_addr = addr;
+    /*
+     * make address point to first available address slot
+     */
+    address = request->he.buf + ADDRS_OFFSET +
+                      (sizeof(struct in_addr) * addr_count);
+    base_address = address;
+
+    /*
+     * find the end of the alias list
+     */
+    alias = hp->h_aliases;
+    while (*alias) {
+      ++alias;
+      ++alias_count;
+    }
+    /*
+     * make name point to first available space in request->buf
+     */
+    if (alias_count > 0) {
+      name = hp->h_aliases[alias_count - 1];
+      name += (strlen(name) + 1);
+    }
+    else if (hp->h_name)
+      name = hp->h_name + strlen(hp->h_name) + 1;
+    else
+      name = request->he.buf + ADDRS_OFFSET + ADDRS_DLEN;
+
+    switch (request->type) {
+    case adns_r_a:
+      for (k = 0; k < answer->nrrs; k++) {
+        if (++addr_count < RES_MAXADDRS) {
+          memcpy(address, &answer->rrs.inaddr[k], sizeof(struct in_addr));
+          *addr++ = address;
+          *addr = 0;
+          address += sizeof(struct in_addr);
+       }
+        Debug((DEBUG_DNS, "Resolver: A %s for %s", ircd_ntoa((char*) &answer->rrs.inaddr[k]), answer->owner));
+      }
+      if (!hp->h_name) {
+        strcpy(name, answer->owner);
+        hp->h_name = name;
+        name += strlen(name) + 1;
+      }
+      break;
+    case adns_r_ptr:
+      strcpy(name, answer->rrs.str[0]);
+      hp->h_name = name;
+      name += strlen(name) + 1;
+
+      Debug((DEBUG_DNS, "Resolver: PTR %s for %s", hp->h_name, answer->owner));
+      break;
+    default:
+      /* ignore */
+      break;
+    }
+
+    if (answer->cname) {
+      alias_count++;
+      ircd_strncpy(name, answer->cname, endp - name);
+      *alias++ = name;
+      *alias   = 0;
+      name += strlen(name) + 1;
+      Debug((DEBUG_DNS, "Resolver: CNAME %s for %s", answer->cname, answer->owner));
+    }
+
+    if (request->type == adns_r_ptr) {
+      struct DNSReply* reply;
+
+      Debug((DEBUG_DNS, "relookup %s <-> %s",
+             request->he.h.h_name, ircd_ntoa((char*) &request->addr)));
+      /*
+       * Lookup the 'authoritive' name that we were given for the
+       * ip#.  By using this call rather than regenerating the
+       * type we automatically gain the use of the cache with no
+       * extra kludges.
+       */
+      reply = gethost_byname(request->he.h.h_name, &request->query);
+      if (reply) {
+        (*request->query.callback)(request->query.vptr, reply);
+      } else {
+        /*
+         * If name wasn't found, a request has been queued and it will
+         * be the last one queued.  This is rather nasty way to keep
+         * a host alias with the query. -avalon
+         */
+        MyFree(requestListTail->he.buf);
+        requestListTail->he.buf = request->he.buf;
+        request->he.buf = 0;
+        memcpy(&requestListTail->he.h, &request->he.h, sizeof(struct hostent));
+      }
+    } else {
+      /*
+       * got a name and address response, client resolved
+       * XXX - Bug found here by Dianora -
+       * make_cache() occasionally returns a NULL pointer when a
+       * PTR returned a CNAME, cp was not checked before so the
+       * callback was being called with a value of 0x2C != NULL.
+       */
+      struct DNSReply* reply = 0;
+      if ((cp = make_cache(request)))
+        reply = &cp->reply;
+      (*request->query.callback)(request->query.vptr, reply);
+    }
+  }
+  
+  rem_request(request);
+  
+  /*
+   * adns doesn't use MyMalloc, so we don't use MyFree
+   */
+  free(answer);
+}
+
+
+static size_t calc_hostent_buffer_size(const struct hostent* hp)
+{
+  char** p;
+  size_t count = 0;
+  assert(0 != hp);
+
+  /*
+   * space for name
+   */
+  count += (strlen(hp->h_name) + 1);
+  /*
+   * space for aliases
+   */
+  for (p = hp->h_aliases; *p; ++p)
+    count += (strlen(*p) + 1 + sizeof(char*));
+  /*
+   * space for addresses
+   */
+  for (p = hp->h_addr_list; *p; ++p)
+    count += (hp->h_length + sizeof(char*));
+  /*
+   * space for 2 nulls to terminate h_aliases and h_addr_list 
+   */
+  count += (2 * sizeof(char*));
+  return count;
+}
+
+
+/*
+ * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
+ * the data we're putting in it.
+ */
+static void dup_hostent(struct Hostent* new_hp, struct hostent* hp)
+{
+  char*  p;
+  char** ap;
+  char** pp;
+  int    alias_count = 0;
+  int    addr_count = 0;
+  size_t bytes_needed = 0;
+
+  assert(0 != new_hp);
+  assert(0 != hp);
+
+  /* how much buffer do we need? */
+  bytes_needed += (strlen(hp->h_name) + 1);
+
+  pp = hp->h_aliases;
+  while (*pp) {
+    bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
+    ++alias_count;
+  }
+  pp = hp->h_addr_list;
+  while (*pp++) {
+    bytes_needed += (hp->h_length + sizeof(char*));
+    ++addr_count;
+  }
+  /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
+  bytes_needed += (2 * sizeof(char*));
+
+  /* Allocate memory */
+  new_hp->buf = (char*) MyMalloc(bytes_needed);
+
+  new_hp->h.h_addrtype = hp->h_addrtype;
+  new_hp->h.h_length = hp->h_length;
+
+  /* first write the address list */
+  pp = hp->h_addr_list;
+  ap = new_hp->h.h_addr_list =
+      (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
+  p = (char*)ap + ((addr_count + 1) * sizeof(char*));
+  while (*pp)
+  {
+    *ap++ = p;
+    memcpy(p, *pp++, hp->h_length);
+    p += hp->h_length;
+  }
+  *ap = 0;
+  /* next write the name */
+  new_hp->h.h_name = p;
+  strcpy(p, hp->h_name);
+  p += (strlen(p) + 1);
+
+  /* last write the alias list */
+  pp = hp->h_aliases;
+  ap = new_hp->h.h_aliases = (char**) new_hp->buf;
+  while (*pp) {
+    *ap++ = p;
+    strcpy(p, *pp++);
+    p += (strlen(p) + 1);
+  }
+  *ap = 0;
+}
+
+/*
+ * update_hostent - Add records to a Hostent struct in place.
+ */
+static void update_hostent(struct Hostent* hp, char** addr, char** alias)
+{
+  char*  p;
+  char** ap;
+  char** pp;
+  int    alias_count = 0;
+  int    addr_count = 0;
+  char*  buf = NULL;
+  size_t bytes_needed = 0;
+
+  if (!hp || !hp->buf)
+    return;
+
+  /* how much buffer do we need? */
+  bytes_needed = strlen(hp->h.h_name) + 1;
+  pp = hp->h.h_aliases;
+  while (*pp) {
+    bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
+    ++alias_count;
+  }
+  if (alias) {
+    pp = alias;
+    while (*pp) {
+      bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
+      ++alias_count;
+    }
+  }
+  pp = hp->h.h_addr_list;
+  while (*pp++) {
+    bytes_needed += (hp->h.h_length + sizeof(char*));
+    ++addr_count;
+  }
+  if (addr) {
+    pp = addr;
+    while (*pp++) {
+      bytes_needed += (hp->h.h_length + sizeof(char*));
+      ++addr_count;
+    }
+  }
+  /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
+  bytes_needed += 2 * sizeof(char*);
+
+  /* Allocate memory */
+  buf = (char*) MyMalloc(bytes_needed);
+  assert(0 != buf);
+
+  /* first write the address list */
+  pp = hp->h.h_addr_list;
+  ap = hp->h.h_addr_list =
+      (char**)(buf + ((alias_count + 1) * sizeof(char*)));
+  p = (char*)ap + ((addr_count + 1) * sizeof(char*));
+  while (*pp) {
+    memcpy(p, *pp++, hp->h.h_length);
+    *ap++ = p;
+    p += hp->h.h_length;
+  }
+  if (addr) {
+    while (*addr) {
+      memcpy(p, *addr++, hp->h.h_length);
+      *ap++ = p;
+      p += hp->h.h_length;
+    }
+  }
+  *ap = 0;
+
+  /* next write the name */
+  strcpy(p, hp->h.h_name);
+  hp->h.h_name = p;
+  p += (strlen(p) + 1);
+
+  /* last write the alias list */
+  pp = hp->h.h_aliases;
+  ap = hp->h.h_aliases = (char**) buf;
+  while (*pp) {
+    strcpy(p, *pp++);
+    *ap++ = p;
+    p += (strlen(p) + 1);
+  }
+  if (alias) {
+    while (*alias) {
+      strcpy(p, *alias++);
+      *ap++ = p;
+      p += (strlen(p) + 1);
+    }
+  }
+  *ap = 0;
+  /* release the old buffer */
+  p = hp->buf;
+  hp->buf = buf;
+  MyFree(p);
+}
+
+/*
+ * hash_number - IP address hash function
+ */
+static int hash_number(const unsigned char* ip)
+{
+  /* could use loop but slower */
+  unsigned int hashv;
+  const u_char* p = (const u_char*) ip;
+
+  assert(0 != p);
+
+  hashv = *p++;
+  hashv += hashv + *p++;
+  hashv += hashv + *p++;
+  hashv += hashv + *p;
+  hashv %= ARES_CACSIZE;
+  return hashv;
+}
+
+/*
+ * hash_name - hostname hash function
+ */
+static int hash_name(const char* name)
+{
+  unsigned int hashv = 0;
+  const u_char* p = (const u_char*) name;
+
+  assert(0 != p);
+
+  for (; *p && *p != '.'; ++p)
+    hashv += *p;
+  hashv %= ARES_CACSIZE;
+  return hashv;
+}
+
+/*
+ * add_to_cache - Add a new cache item to the queue and hash table.
+ */
+static struct CacheEntry* add_to_cache(struct CacheEntry* ocp)
+{
+  int  hashv;
+
+  assert(0 != ocp);
+
+  ocp->list_next = cacheTop;
+  cacheTop = ocp;
+
+  hashv = hash_name(ocp->he.h.h_name);
+
+  ocp->hname_next = hashtable[hashv].name_list;
+  hashtable[hashv].name_list = ocp;
+
+  hashv = hash_number((const unsigned char*) ocp->he.h.h_addr);
+
+  ocp->hnum_next = hashtable[hashv].num_list;
+  hashtable[hashv].num_list = ocp;
+
+  /*
+   * LRU deletion of excessive cache entries.
+   */
+  if (++cachedCount > MAXCACHED) {
+    struct CacheEntry* cp;
+    struct CacheEntry* cp_next;
+    for (cp = ocp->list_next; cp; cp = cp_next) {
+      cp_next = cp->list_next;
+      rem_cache(cp);
+    }
+  }
+  ++cainfo.ca_adds;
+  return ocp;
+}
+
+/*
+ * update_list - does not alter the cache structure passed. It is assumed that
+ * it already contains the correct expire time, if it is a new entry. Old
+ * entries have the expirey time updated.
+*/
+static void update_list(struct ResRequest* request, struct CacheEntry* cachep)
+{
+  struct CacheEntry*  cp = cachep;
+  char*    s;
+  char*    t;
+  int      i;
+  int      j;
+  char**   ap;
+  char*    addrs[RES_MAXADDRS + 1];
+  char*    aliases[RES_MAXALIASES + 1];
+
+  /*
+   * search for the new cache item in the cache list by hostname.
+   * If found, move the entry to the top of the list and return.
+   */
+  ++cainfo.ca_updates;
+
+  if (!request)
+    return;
+  /*
+   * Compare the cache entry against the new record.  Add any
+   * previously missing names for this entry.
+   */
+  *aliases = 0;
+  ap = aliases;
+  for (i = 0, s = request->he.h.h_name; s; s = request->he.h.h_aliases[i++]) {
+    for (j = 0, t = cp->he.h.h_name; t; t = cp->he.h.h_aliases[j++]) {
+      if (0 == ircd_strcmp(t, s))
+        break;
+    }
+    if (!t) {
+      *ap++ = s;
+      *ap = 0;
+    }
+  }
+  /*
+   * Do the same again for IP#'s.
+   */
+  *addrs = 0;
+  ap = addrs;
+  for (i = 0; (s = request->he.h.h_addr_list[i]); i++) {
+    for (j = 0; (t = cp->he.h.h_addr_list[j]); j++) {
+      if (!memcmp(t, s, sizeof(struct in_addr)))
+        break;
+    }
+    if (!t) {
+      *ap++ = s;
+      *ap = 0;
+    }
+  }
+  if (*addrs || *aliases)
+    update_hostent(&cp->he, addrs, aliases);
+}
+
+/*
+ * find_cache_name - find name in nameserver cache
+ */
+static struct CacheEntry* find_cache_name(const char* name)
+{
+  struct CacheEntry* cp;
+  char*   s;
+  int     hashv;
+  int     i;
+
+  assert(0 != name);
+  hashv = hash_name(name);
+
+  cp = hashtable[hashv].name_list;
+
+  for (; cp; cp = cp->hname_next) {
+    for (i = 0, s = cp->he.h.h_name; s; s = cp->he.h.h_aliases[i++]) {
+      if (0 == ircd_strcmp(s, name)) {
+        ++cainfo.ca_na_hits;
+        return cp;
+      }
+    }
+  }
+
+  for (cp = cacheTop; cp; cp = cp->list_next) {
+    /*
+     * if no aliases or the hash value matches, we've already
+     * done this entry and all possiblilities concerning it.
+     */
+    if (!cp->he.h.h_name || hashv == hash_name(cp->he.h.h_name))
+      continue;
+    for (i = 0, s = cp->he.h.h_aliases[i]; s; s = cp->he.h.h_aliases[++i]) {
+      if (0 == ircd_strcmp(name, s)) {
+        ++cainfo.ca_na_hits;
+        return cp;
+      }
+    }
+  }
+  return NULL;
+}
+
+/*
+ * find_cache_number - find a cache entry by ip# and update its expire time
+ */
+static struct CacheEntry* find_cache_number(struct ResRequest* request,
+                                            const char* addr)
+{
+  struct CacheEntry* cp;
+  int     hashv;
+  int     i;
+
+  assert(0 != addr);
+  hashv = hash_number((const unsigned char*) addr);
+  cp = hashtable[hashv].num_list;
+
+  for (; cp; cp = cp->hnum_next) {
+    for (i = 0; cp->he.h.h_addr_list[i]; ++i) {
+      if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
+        ++cainfo.ca_nu_hits;
+        return cp;
+      }
+    }
+  }
+  for (cp = cacheTop; cp; cp = cp->list_next) {
+    /*
+     * single address entry...would have been done by hashed
+     * search above...
+     * if the first IP# has the same hashnumber as the IP# we
+     * are looking for, its been done already.
+     */
+    if (!cp->he.h.h_addr_list[1] || 
+        hashv == hash_number((const unsigned char*) cp->he.h.h_addr_list[0]))
+      continue;
+    for (i = 1; cp->he.h.h_addr_list[i]; ++i) {
+      if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
+        ++cainfo.ca_nu_hits;
+        return cp;
+      }
+    }
+  }
+  return NULL;
+}
+
+static struct CacheEntry* make_cache(struct ResRequest* request)
+{
+  struct CacheEntry* cp;
+  int     i;
+  struct hostent* hp;
+  assert(0 != request);
+
+  hp = &request->he.h;
+  /*
+   * shouldn't happen but it just might...
+   */
+  assert(0 != hp->h_name);
+/*    assert(0 != hp->h_addr_list[0]); */
+  if (!hp->h_name || !hp->h_addr_list[0])
+    return NULL;
+  /*
+   * Make cache entry.  First check to see if the cache already exists
+   * and if so, return a pointer to it.
+   */
+  for (i = 0; hp->h_addr_list[i]; ++i) {
+    if ((cp = find_cache_number(request, hp->h_addr_list[i]))) {
+      update_list(request, cp);
+      return cp;
+    }
+  }
+  /*
+   * a matching entry wasnt found in the cache so go and make one up.
+   */ 
+  cp = (struct CacheEntry*) MyMalloc(sizeof(struct CacheEntry));
+  assert(0 != cp);
+
+  memset(cp, 0, sizeof(struct CacheEntry));
+  dup_hostent(&cp->he, hp);
+  cp->reply.hp = &cp->he.h;
+  /*
+   * hmmm... we could time out the cache after 10 minutes regardless
+   * would that be reasonable since we don't save the reply?
+   */ 
+#if 0
+  if (request->ttl < AR_TTL) {
+    ++reinfo.re_shortttl;
+    cp->ttl = AR_TTL;
+  }
+  else
+    cp->ttl = request->ttl;
+#else
+  cp->ttl = AR_TTL;
+#endif
+  cp->expireat = CurrentTime + cp->ttl;
+  return add_to_cache(cp);
+}
+
+/*
+ * rem_cache - delete a cache entry from the cache structures 
+ * and lists and return all memory used for the cache back to the memory pool.
+ */
+static void rem_cache(struct CacheEntry* ocp)
+{
+  struct CacheEntry** cp;
+  int                 hashv;
+  struct hostent*     hp;
+  assert(0 != ocp);
+
+
+  if (0 < ocp->reply.ref_count) {
+    if (ocp->expireat < CurrentTime) {
+      ocp->expireat = CurrentTime + AR_TTL;
+      Debug((DEBUG_DNS, "Resolver: referenced cache entry not removed for: %s",
+            ocp->he.h.h_name));
+    }
+    return;
+  }
+  /*
+   * remove cache entry from linked list
+   */
+  for (cp = &cacheTop; *cp; cp = &((*cp)->list_next)) {
+    if (*cp == ocp) {
+      *cp = ocp->list_next;
+      break;
+    }
+  }
+  hp = &ocp->he.h;
+  /*
+   * remove cache entry from hashed name list
+   */
+  assert(0 != hp->h_name);
+  hashv = hash_name(hp->h_name);
+
+  for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next)) {
+    if (*cp == ocp) {
+      *cp = ocp->hname_next;
+      break;
+    }
+  }
+  /*
+   * remove cache entry from hashed number list
+   */
+  hashv = hash_number((const unsigned char*) hp->h_addr);
+  assert(-1 < hashv);
+
+  for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next)) {
+    if (*cp == ocp) {
+      *cp = ocp->hnum_next;
+      break;
+    }
+  }
+  /*
+   * free memory used to hold the various host names and the array
+   * of alias pointers.
+   */
+  MyFree(ocp->he.buf);
+  MyFree(ocp);
+  --cachedCount;
+  ++cainfo.ca_dels;
+}
+
+void flush_resolver_cache(void)
+{
+  /*
+   * stubbed - iterate cache and remove everything that isn't referenced
+   */
+}
+
+/*
+ * m_dns - dns status query
+ */
+int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
+{
+#if !defined(NDEBUG)
+  struct CacheEntry* cp;
+  int     i;
+  struct hostent* hp;
+
+  if (parv[1] && *parv[1] == 'l') {
+    for(cp = cacheTop; cp; cp = cp->list_next) {
+      hp = &cp->he.h;
+      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Ex %d ttl %d host %s(%s)",
+                   sptr, cp->expireat - CurrentTime, cp->ttl,
+                   hp->h_name, ircd_ntoa(hp->h_addr));
+      for (i = 0; hp->h_aliases[i]; i++)
+        sendcmdto_one(&me, CMD_NOTICE, sptr, "%C : %s = %s (CN)", sptr,
+                     hp->h_name, hp->h_aliases[i]);
+      for (i = 1; hp->h_addr_list[i]; i++)
+        sendcmdto_one(&me, CMD_NOTICE, sptr, "%C : %s = %s (IP)", sptr,
+                     hp->h_name, ircd_ntoa(hp->h_addr_list[i]));
+    }
+    return 0;
+  }
+  if (parv[1] && *parv[1] == 'd') {
+    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :ResolverFileDescriptor = %d", 
+                 sptr, ResolverFileDescriptor);
+    return 0;
+  }
+  sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ca %d Cd %d Ce %d Cl %d Ch %d:%d "
+               "Cu %d", sptr,
+               cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires,
+               cainfo.ca_lookups, cainfo.ca_na_hits, cainfo.ca_nu_hits, 
+               cainfo.ca_updates);
+  
+  sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Re %d Rl %d/%d Rp %d Rq %d",
+               sptr, reinfo.re_errors, reinfo.re_nu_look,
+               reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
+  sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr,
+               reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
+               reinfo.re_resends, reinfo.re_timeouts);
+#endif
+  return 0;
+}
+
+size_t cres_mem(struct Client* sptr)
+{
+  struct CacheEntry* entry;
+  struct ResRequest* request;
+  size_t cache_mem     = 0;
+  size_t request_mem   = 0;
+  int    cache_count   = 0;
+  int    request_count = 0;
+
+  for (entry = cacheTop; entry; entry = entry->list_next) {
+    cache_mem += sizeof(struct CacheEntry);
+    cache_mem += calc_hostent_buffer_size(&entry->he.h); 
+    ++cache_count;
+  }
+  for (request = requestListHead; request; request = request->next) {
+    request_mem += sizeof(struct ResRequest);
+    if (request->name)
+      request_mem += strlen(request->name) + 1; 
+    if (request->he.buf)
+      request_mem += MAXGETHOSTLEN + 1;
+    ++request_count;
+  }
+
+  if (cachedCount != cache_count) {
+    send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
+              ":Resolver: cache count mismatch: %d != %d", cachedCount,
+              cache_count);
+    assert(cachedCount == cache_count);
+  }
+  send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
+            ":Resolver: cache %d(%d) requests %d(%d)", cache_count,
+            cache_mem, request_count, request_mem);
+  return cache_mem + request_mem;
+}
+
diff --git a/ircd/res_libresolv.c b/ircd/res_libresolv.c
new file mode 100644 (file)
index 0000000..d4d85fd
--- /dev/null
@@ -0,0 +1,1803 @@
+/*
+ * src/res.c (C)opyright 1992 Darren Reed. All rights reserved.
+ * This file may not be distributed without the author's permission in any
+ * shape or form. The author takes no responsibility for any damage or loss
+ * of property which results from the use of this software.
+ *
+ * $Id$
+ *
+ * July 1999 - Rewrote a bunch of stuff here. Change hostent builder code,
+ *     added callbacks and reference counting of returned hostents.
+ *     --Bleep (Thomas Helvey <tomh@inxpress.net>)
+ */
+#include "config.h"
+
+#include "res.h"
+#include "client.h"
+#include "ircd.h"
+#include "ircd_alloc.h"
+#include "ircd_events.h"
+#include "ircd_log.h"
+#include "ircd_osdep.h"
+#include "ircd_reply.h"
+#include "ircd_snprintf.h"
+#include "ircd_string.h"
+#include "msg.h"
+#include "numeric.h"
+#include "s_bsd.h"
+#include "s_debug.h"
+#include "s_misc.h"
+#include "send.h"
+#include "struct.h"
+#include "support.h"
+#include "sys.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <regex.h>
+
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <limits.h>
+#if (CHAR_BIT != 8)
+#error this code needs to be able to address individual octets 
+#endif
+
+/*
+ * Some systems do not define INADDR_NONE (255.255.255.255)
+ * INADDR_NONE is actually a valid address, but it should never
+ * be returned from any nameserver.
+ * NOTE: The bit pattern for INADDR_NONE and INADDR_ANY (0.0.0.0) should be 
+ * the same on all hosts so we shouldn't need to use htonl or ntohl to
+ * compare or set the values.
+ */ 
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned int) 0xffffffff)
+#endif
+
+#define MAXPACKET       1024  /* rfc sez 512 but we expand names so ... */
+#define RES_MAXALIASES  35    /* maximum aliases allowed */
+#define RES_MAXADDRS    35    /* maximum addresses allowed */
+/*
+ * OSF1 doesn't have RES_NOALIASES
+ */
+#ifndef RES_NOALIASES
+#define RES_NOALIASES 0
+#endif
+
+/*
+ * macros used to calulate offsets into fixed query buffer
+ */
+#define ALIAS_BLEN  ((RES_MAXALIASES + 1) * sizeof(char*))
+#define ADDRS_BLEN  ((RES_MAXADDRS + 1) * sizeof(struct in_addr*))
+
+#define ADDRS_OFFSET   (ALIAS_BLEN + ADDRS_BLEN)
+#define ADDRS_DLEN     (RES_MAXADDRS * sizeof(struct in_addr))
+#define NAMES_OFFSET   (ADDRS_OFFSET + ADDRS_DLEN)
+#define MAXGETHOSTLEN  (NAMES_OFFSET + MAXPACKET)
+
+#define AR_TTL          600   /* TTL in seconds for dns cache entries */
+
+/*
+ * the following values should be prime
+ */
+#define ARES_CACSIZE    307
+#define MAXCACHED       281
+
+/*
+ * RFC 1104/1105 wasn't very helpful about what these fields
+ * should be named, so for now, we'll just name them this way.
+ * we probably should look at what named calls them or something.
+ */
+#define TYPE_SIZE       2
+#define CLASS_SIZE      2
+#define TTL_SIZE        4
+#define RDLENGTH_SIZE   2
+#define ANSWER_FIXED_SIZE (TYPE_SIZE + CLASS_SIZE + TTL_SIZE + RDLENGTH_SIZE)
+
+/*
+ * Building the Hostent
+ * The Hostent struct is arranged like this:
+ *          +-------------------------------+
+ * Hostent: | struct hostent h              |
+ *          |-------------------------------|
+ *          | char *buf                     |
+ *          +-------------------------------+
+ *
+ * allocated:
+ *
+ *          +-------------------------------+
+ * buf:     | h_aliases pointer array       | Max size: ALIAS_BLEN;
+ *          | NULL                          | contains `char *'s
+ *          |-------------------------------|
+ *          | h_addr_list pointer array     | Max size: ADDRS_BLEN;
+ *          | NULL                          | contains `struct in_addr *'s
+ *          |-------------------------------|
+ *          | h_addr_list addresses         | Max size: ADDRS_DLEN;
+ *          |                               | contains `struct in_addr's
+ *          |-------------------------------|
+ *          | storage for hostname strings  | Max size: ALIAS_DLEN;
+ *          +-------------------------------+ contains `char's
+ *
+ *  For requests the size of the h_aliases, and h_addr_list pointer
+ *  array sizes are set to MAXALISES and MAXADDRS respectively, and
+ *  buf is a fixed size with enough space to hold the largest expected
+ *  reply from a nameserver, see RFC 1034 and RFC 1035.
+ *  For cached entries the sizes are dependent on the actual number
+ *  of aliases and addresses. If new aliases and addresses are found
+ *  for cached entries, the buffer is grown and the new entries are added.
+ *  The hostent struct is filled in with the addresses of the entries in
+ *  the Hostent buf as follows:
+ *  h_name      - contains a pointer to the start of the hostname string area,
+ *                or NULL if none is set.  The h_name is followed by the
+ *                aliases, in the storage for hostname strings area.
+ *  h_aliases   - contains a pointer to the start of h_aliases pointer array.
+ *                This array contains pointers to the storage for hostname
+ *                strings area and is terminated with a NULL.  The first alias
+ *                is stored directly after the h_name.
+ *  h_addr_list - contains a pointer to the start of h_addr_list pointer array.
+ *                This array contains pointers to in_addr structures in the
+ *                h_addr_list addresses area and is terminated with a NULL.
+ *
+ *  Filling the buffer this way allows for proper alignment of the h_addr_list
+ *  addresses.
+ *
+ *  This arrangement allows us to alias a Hostent struct pointer as a
+ *  real struct hostent* without lying. It also allows us to change the
+ *  values contained in the cached entries and requests without changing
+ *  the actual hostent pointer, which is saved in a client struct and can't
+ *  be changed without blowing things up or a lot more fiddling around.
+ *  It also allows for defered allocation of the fixed size buffers until
+ *  they are really needed.
+ *  Nov. 17, 1997 --Bleep
+ */
+
+struct Hostent {
+  struct hostent h;      /* the hostent struct we are passing around */
+  char*          buf;    /* buffer for data pointed to from hostent */
+};
+
+struct ResRequest {
+  struct ResRequest* next;
+  int                id;
+  int                sent;          /* number of requests sent */
+  time_t             ttl;
+  char               type;
+  char               retries;       /* retry counter */
+  char               sends;         /* number of sends (>1 means resent) */
+  char               resend;        /* send flag. 0 == dont resend */
+  time_t             sentat;
+  time_t             timeout;
+  struct in_addr     addr;
+  char*              name;
+  struct DNSQuery    query;         /* query callback for this request */
+  struct Hostent     he;
+};
+
+struct CacheEntry {
+  struct CacheEntry* hname_next;
+  struct CacheEntry* hnum_next;
+  struct CacheEntry* list_next;
+  time_t             expireat;
+  time_t             ttl;
+  struct Hostent     he;
+  struct DNSReply    reply;
+};
+
+struct CacheTable {
+  struct CacheEntry* num_list;
+  struct CacheEntry* name_list;
+};
+
+
+int ResolverFileDescriptor    = -1;   /* GLOBAL - used in s_bsd.c */
+
+static struct Socket resSock;          /* Socket describing resolver */
+static struct Timer  resExpireDNS;     /* Timer for DNS expiration */
+static struct Timer  resExpireCache;   /* Timer for cache expiration */
+
+static time_t nextDNSCheck    = 0;
+static time_t nextCacheExpire = 1;
+
+/*
+ * Keep a spare file descriptor open. res_init calls fopen to read the
+ * resolv.conf file. If ircd is hogging all the file descriptors below 256,
+ * on systems with crippled FILE structures this will cause wierd bugs.
+ * This is definitely needed for Solaris which uses an unsigned char to
+ * hold the file descriptor.  --Dianora
+ */ 
+static int                spare_fd = -1;
+
+static int                cachedCount = 0;
+static struct CacheTable  hashtable[ARES_CACSIZE];
+static struct CacheEntry* cacheTop;
+static struct ResRequest* requestListHead;   /* head of resolver request list */
+static struct ResRequest* requestListTail;   /* tail of resolver request list */
+
+
+static void     add_request(struct ResRequest* request);
+static void     rem_request(struct ResRequest* request);
+static struct ResRequest*   make_request(const struct DNSQuery* query);
+static time_t   timeout_query_list(time_t now);
+static time_t   expire_cache(time_t now);
+static void     rem_cache(struct CacheEntry*);
+static void     do_query_name(const struct DNSQuery* query, 
+                              const char* name, 
+                              struct ResRequest* request);
+static void     do_query_number(const struct DNSQuery* query,
+                                const struct in_addr*, 
+                                struct ResRequest* request);
+static void     query_name(const char* name, 
+                           int query_class, 
+                           int query_type, 
+                           struct ResRequest* request);
+static void     resend_query(struct ResRequest* request);
+static struct CacheEntry*  make_cache(struct ResRequest* request);
+static struct CacheEntry*  find_cache_name(const char* name);
+static struct CacheEntry*  find_cache_number(struct ResRequest* request, 
+                                             const char* addr);
+static struct ResRequest*   find_id(int);
+
+static struct cacheinfo {
+  int  ca_adds;
+  int  ca_dels;
+  int  ca_expires;
+  int  ca_lookups;
+  int  ca_na_hits;
+  int  ca_nu_hits;
+  int  ca_updates;
+} cainfo;
+
+static  struct  resinfo {
+  int  re_errors;
+  int  re_nu_look;
+  int  re_na_look;
+  int  re_replies;
+  int  re_requests;
+  int  re_resends;
+  int  re_sent;
+  int  re_timeouts;
+  int  re_shortttl;
+  int  re_unkrep;
+} reinfo;
+
+
+/*
+ * From bind 8.3, these aren't declared in earlier versions of bind
+ */
+extern u_short  _getshort(const u_char *);
+extern u_int    _getlong(const u_char *);
+/*
+ * int
+ * res_isourserver(ina)
+ *      looks up "ina" in _res.ns_addr_list[]
+ * returns:
+ *      0  : not found
+ *      >0 : found
+ * author:
+ *      paul vixie, 29may94
+ */
+static int
+res_ourserver(const struct __res_state* statp, const struct sockaddr_in* inp) 
+{
+  struct sockaddr_in ina;
+  int ns;
+
+  ina = *inp;
+  for (ns = 0;  ns < statp->nscount;  ns++) {
+    const struct sockaddr_in *srv = &statp->nsaddr_list[ns];
+
+    if (srv->sin_family == ina.sin_family &&
+         srv->sin_port == ina.sin_port &&
+         (srv->sin_addr.s_addr == INADDR_ANY ||
+          srv->sin_addr.s_addr == ina.sin_addr.s_addr))
+             return (1);
+  }
+  return (0);
+}
+
+/* Socket callback for resolver */
+static void res_callback(struct Event* ev)
+{
+  assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
+
+  resolver_read();
+}
+
+/*
+ * start_resolver - do everything we need to read the resolv.conf file
+ * and initialize the resolver file descriptor if needed
+ */
+static void start_resolver(void)
+{
+  Debug((DEBUG_DNS, "Resolver: start_resolver"));
+  /*
+   * close the spare file descriptor so res_init can read resolv.conf
+   * successfully. Needed on Solaris
+   */
+  if (spare_fd > -1)
+    close(spare_fd);
+
+  res_init();      /* res_init always returns 0 */
+  /*
+   * make sure we have a valid file descriptor below 256 so we can
+   * do this again. Needed on Solaris
+   */
+  spare_fd = open("/dev/null",O_RDONLY,0);
+  if ((spare_fd < 0) || (spare_fd > 255)) {
+    char sparemsg[80];
+    ircd_snprintf(0, sparemsg, sizeof(sparemsg), "invalid spare_fd %d",
+                 spare_fd);
+    server_restart(sparemsg);
+  }
+
+  if (!_res.nscount) {
+    _res.nscount = 1;
+    _res.nsaddr_list[0].sin_addr.s_addr = inet_addr("127.0.0.1");
+  }
+  _res.options |= RES_NOALIASES;
+
+  if (ResolverFileDescriptor < 0) {
+    ResolverFileDescriptor = socket(AF_INET, SOCK_DGRAM, 0);
+    if (-1 == ResolverFileDescriptor) {
+      report_error("Resolver: error creating socket for %s: %s", 
+                   cli_name(&me), errno);
+      return;
+    }
+    if (!os_set_nonblocking(ResolverFileDescriptor))
+      report_error("Resolver: error setting non-blocking for %s: %s", 
+                   cli_name(&me), errno);
+    if (!socket_add(&resSock, res_callback, 0, SS_DATAGRAM,
+                   SOCK_EVENT_READABLE, ResolverFileDescriptor))
+      report_error("Resolver: unable to queue resolver file descriptor for %s",
+                  cli_name(&me), ENFILE);
+  }
+}
+
+/* Call the query timeout function */
+static void expire_DNS_callback(struct Event* ev)
+{
+  time_t next;
+
+  next = timeout_query_list(CurrentTime);
+
+  timer_add(&resExpireDNS, expire_DNS_callback, 0, TT_ABSOLUTE, next);
+}
+
+/* Call the cache expire function */
+static void expire_cache_callback(struct Event* ev)
+{
+  time_t next;
+
+  next = expire_cache(CurrentTime);
+
+  timer_add(&resExpireCache, expire_cache_callback, 0, TT_ABSOLUTE, next);
+}
+
+/*
+ * init_resolver - initialize resolver and resolver library
+ */
+int init_resolver(void)
+{
+  Debug((DEBUG_DNS, "Resolver: init_resolver"));
+#ifdef  LRAND48
+  srand48(CurrentTime);
+#endif
+  memset(&cainfo,   0, sizeof(cainfo));
+  memset(hashtable, 0, sizeof(hashtable));
+  memset(&reinfo,   0, sizeof(reinfo));
+
+  requestListHead = requestListTail = 0;
+
+  /* initiate the resolver timers */
+  timer_add(timer_init(&resExpireDNS), expire_DNS_callback, 0,
+           TT_RELATIVE, 1);
+  timer_add(timer_init(&resExpireCache), expire_cache_callback, 0,
+           TT_RELATIVE, 1);
+
+  errno = h_errno = 0;
+
+  start_resolver();
+  Debug((DEBUG_DNS, "Resolver: fd %d errno: %d h_errno: %d: %s",
+         ResolverFileDescriptor, errno, h_errno, 
+         (strerror(errno)) ? strerror(errno) : "Unknown"));
+  return ResolverFileDescriptor;
+}
+
+/*
+ * restart_resolver - flush the cache, reread resolv.conf, reopen socket
+ */
+void restart_resolver(void)
+{
+  /* flush_cache();  flush the dns cache */
+  start_resolver();
+}
+
+static int validate_hostent(const struct hostent* hp)
+{
+  const char* name;
+  int  i = 0;
+  assert(0 != hp);
+  for (name = hp->h_name; name; name = hp->h_aliases[i++]) {
+    if (!string_is_hostname(name))
+      return 0;
+  }
+  return 1;
+}
+
+/*
+ * add_request - place a new request in the request list
+ */
+static void add_request(struct ResRequest* request)
+{
+  assert(0 != request);
+  if (!requestListHead)
+    requestListHead = requestListTail = request;
+  else {
+    requestListTail->next = request;
+    requestListTail = request;
+  }
+  request->next = NULL;
+  ++reinfo.re_requests;
+}
+
+/*
+ * rem_request - remove a request from the list. 
+ * This must also free any memory that has been allocated for 
+ * temporary storage of DNS results.
+ */
+static void rem_request(struct ResRequest* request)
+{
+  struct ResRequest** current;
+  struct ResRequest*  prev = NULL;
+
+  assert(0 != request);
+  for (current = &requestListHead; *current; ) {
+    if (*current == request) {
+      *current = request->next;
+      if (requestListTail == request)
+        requestListTail = prev;
+      break;
+    } 
+    prev    = *current;
+    current = &(*current)->next;
+  }
+  MyFree(request->he.buf);
+  MyFree(request->name);
+  MyFree(request);
+}
+
+/*
+ * make_request - Create a DNS request record for the server.
+ */
+static struct ResRequest* make_request(const struct DNSQuery* query)
+{
+  struct ResRequest* request;
+  assert(0 != query);
+  request = (struct ResRequest*) MyMalloc(sizeof(struct ResRequest));
+  memset(request, 0, sizeof(struct ResRequest));
+
+  request->sentat           = CurrentTime;
+  request->retries          = 3;
+  request->resend           = 1;
+  request->timeout          = 5;    /* start at 5 per RFC1123 */
+  request->addr.s_addr      = INADDR_NONE;
+  request->he.h.h_addrtype  = AF_INET;
+  request->he.h.h_length    = sizeof(struct in_addr);
+  request->query.vptr       = query->vptr;
+  request->query.callback   = query->callback;
+
+#if defined(NULL_POINTER_NOT_ZERO)
+  request->next             = NULL;
+  request->he.buf           = NULL;
+  request->he.h.h_name      = NULL;
+  request->he.h.h_aliases   = NULL;
+  request->he.h.h_addr_list = NULL;
+#endif
+  add_request(request);
+  return request;
+}
+
+/*
+ * timeout_query_list - Remove queries from the list which have been 
+ * there too long without being resolved.
+ */
+static time_t timeout_query_list(time_t now)
+{
+  struct ResRequest* request;
+  struct ResRequest* next_request = 0;
+  time_t             next_time    = 0;
+  time_t             timeout      = 0;
+
+  Debug((DEBUG_DNS, "Resolver: timeout_query_list at %s", myctime(now)));
+  for (request = requestListHead; request; request = next_request) {
+    next_request = request->next;
+    timeout = request->sentat + request->timeout;
+    if (timeout < now) {
+      if (--request->retries <= 0) {
+        ++reinfo.re_timeouts;
+        (*request->query.callback)(request->query.vptr, 0);
+        rem_request(request);
+        continue;
+      }
+      else {
+        request->sentat = now;
+        request->timeout += request->timeout;
+        resend_query(request);
+      }
+    }
+    if (!next_time || timeout < next_time) {
+      next_time = timeout;
+    }
+  }
+  return (next_time > now) ? next_time : (now + AR_TTL);
+}
+
+/*
+ * expire_cache - removes entries from the cache which are older 
+ * than their expiry times. returns the time at which the server 
+ * should next poll the cache.
+ */
+static time_t expire_cache(time_t now)
+{
+  struct CacheEntry* cp;
+  struct CacheEntry* cp_next;
+  time_t             expire = 0;
+
+  Debug((DEBUG_DNS, "Resolver: expire_cache at %s", myctime(now)));
+  for (cp = cacheTop; cp; cp = cp_next) {
+    cp_next = cp->list_next;
+    if (cp->expireat < now) {
+      ++cainfo.ca_expires;
+      rem_cache(cp);
+    }
+    else if (!expire || expire > cp->expireat)
+      expire = cp->expireat;
+  }
+  return (expire > now) ? expire : (now + AR_TTL);
+}
+
+/*
+ * timeout_resolver - check request list and cache for expired entries
+ */
+time_t timeout_resolver(time_t now)
+{
+  if (nextDNSCheck < now)
+    nextDNSCheck = timeout_query_list(now);
+  if (nextCacheExpire < now)
+    nextCacheExpire = expire_cache(now);
+  return IRCD_MIN(nextDNSCheck, nextCacheExpire);
+}
+
+
+/*
+ * delete_resolver_queries - cleanup outstanding queries 
+ * for which there no longer exist clients or conf lines.
+ */
+void delete_resolver_queries(const void* vptr)
+{
+  struct ResRequest* request;
+  struct ResRequest* next_request;
+
+  for (request = requestListHead; request; request = next_request) {
+    next_request = request->next;
+    if (vptr == request->query.vptr)
+      rem_request(request);
+  }
+}
+
+/*
+ * send_res_msg - sends msg to all nameservers found in the "_res" structure.
+ * This should reflect /etc/resolv.conf. We will get responses
+ * which arent needed but is easier than checking to see if nameserver
+ * isnt present. Returns number of messages successfully sent to 
+ * nameservers or -1 if no successful sends.
+ */
+static int send_res_msg(const u_char* msg, int len, int rcount)
+{
+  int i;
+  int sent = 0;
+  int max_queries = IRCD_MIN(_res.nscount, rcount);
+
+  assert(0 != msg);
+  /*
+   * RES_PRIMARY option is not implemented
+   * if (_res.options & RES_PRIMARY || 0 == max_queries)
+   */
+  if (0 == max_queries)
+    max_queries = 1;
+
+  Debug((DEBUG_DNS, "Resolver: sendto %d", max_queries));
+
+  for (i = 0; i < max_queries; i++) {
+    if (sendto(ResolverFileDescriptor, msg, len, 0, 
+               (struct sockaddr*) &(_res.nsaddr_list[i]),
+               sizeof(struct sockaddr_in)) == len) {
+      ++reinfo.re_sent;
+      ++sent;
+    }
+    else
+      log_write(LS_RESOLVER, L_ERROR, 0, "Resolver: send failed %m");
+  }
+  return sent;
+}
+
+/*
+ * find_id - find a dns request id (id is determined by dn_mkquery)
+ */
+static struct ResRequest* find_id(int id)
+{
+  struct ResRequest* request;
+
+  for (request = requestListHead; request; request = request->next) {
+    if (request->id == id)
+      return request;
+  }
+  return NULL;
+}
+
+/*
+ * gethost_byname - get host address from name
+ */
+struct DNSReply* gethost_byname(const char* name, 
+                               const struct DNSQuery* query)
+{
+  struct CacheEntry* cp;
+  assert(0 != name);
+
+  Debug((DEBUG_DNS, "Resolver: gethost_byname %s", name));
+  ++reinfo.re_na_look;
+  if ((cp = find_cache_name(name)))
+    return &(cp->reply);
+
+  do_query_name(query, name, NULL);
+  nextDNSCheck = 1;
+  return NULL;
+}
+
+/*
+ * gethost_byaddr - get host name from address
+ */
+struct DNSReply* gethost_byaddr(const char* addr,
+                                const struct DNSQuery* query)
+{
+  struct CacheEntry *cp;
+
+  assert(0 != addr);
+
+  Debug((DEBUG_DNS, "Resolver: gethost_byaddr %s", ircd_ntoa(addr)));
+
+  ++reinfo.re_nu_look;
+  if ((cp = find_cache_number(NULL, addr)))
+    return &(cp->reply);
+
+  do_query_number(query, (const struct in_addr*) addr, NULL);
+  nextDNSCheck = 1;
+  return NULL;
+}
+
+/*
+ * do_query_name - nameserver lookup name
+ */
+static void do_query_name(const struct DNSQuery* query, 
+                          const char* name, struct ResRequest* request)
+{
+  char  hname[HOSTLEN + 1];
+  assert(0 != name);
+
+  ircd_strncpy(hname, name, HOSTLEN);
+  hname[HOSTLEN] = '\0';
+
+  if (!request) {
+    request       = make_request(query);
+    request->type = T_A;
+    request->name = (char*) MyMalloc(strlen(hname) + 1);
+    strcpy(request->name, hname);
+  }
+  query_name(hname, C_IN, T_A, request);
+}
+
+/*
+ * do_query_number - Use this to do reverse IP# lookups.
+ */
+static void do_query_number(const struct DNSQuery* query, 
+                            const struct in_addr* addr,
+                            struct ResRequest* request)
+{
+  char  ipbuf[32];
+  const unsigned char* cp;
+
+  assert(0 != addr);
+  cp = (const unsigned char*) &addr->s_addr;
+  ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%u.%u.%u.%u.in-addr.arpa.",
+               (unsigned int)(cp[3]), (unsigned int)(cp[2]),
+               (unsigned int)(cp[1]), (unsigned int)(cp[0]));
+
+  if (!request) {
+    request              = make_request(query);
+    request->type        = T_PTR;
+    request->addr.s_addr = addr->s_addr;
+  }
+  query_name(ipbuf, C_IN, T_PTR, request);
+}
+
+/*
+ * query_name - generate a query based on class, type and name.
+ */
+static void query_name(const char* name, int query_class,
+                       int type, struct ResRequest* request)
+{
+  char buf[MAXPACKET];
+  int  request_len = 0;
+
+  assert(0 != name);
+  assert(0 != request);
+
+  Debug((DEBUG_DNS, "Resolver: query_name: %s %d %d", name, query_class, type));
+  memset(buf, 0, sizeof(buf));
+  if ((request_len = res_mkquery(QUERY, name, query_class, type, 
+                                 0, 0, 0, (unsigned char*) buf, sizeof(buf))) > 0) {
+    HEADER* header = (HEADER*) buf;
+#ifndef LRAND48
+    int            k = 0;
+    struct timeval tv;
+#endif
+    /*
+     * generate a unique id
+     * NOTE: we don't have to worry about converting this to and from
+     * network byte order, the nameserver does not interpret this value
+     * and returns it unchanged
+     */
+#ifdef LRAND48
+    do {
+      header->id = (header->id + lrand48()) & 0xffff;
+    } while (find_id(header->id));
+#else
+    gettimeofday(&tv, NULL);
+    do {
+      header->id = (header->id + k + tv.tv_usec) & 0xffff;
+      ++k;
+    } while (find_id(header->id));
+#endif /* LRAND48 */
+    request->id = header->id;
+    ++request->sends;
+    Debug((DEBUG_DNS, "Resolver: query_name %d: %s %d %d", request->id, 
+          name, query_class, type));
+    request->sent += send_res_msg((const unsigned char*) buf, request_len, request->sends);
+  }
+}
+
+static void resend_query(struct ResRequest* request)
+{
+  assert(0 != request);
+
+  if (request->resend == 0)
+    return;
+  ++reinfo.re_resends;
+  switch(request->type) {
+  case T_PTR:
+    do_query_number(NULL, &request->addr, request);
+    break;
+  case T_A:
+    do_query_name(NULL, request->name, request);
+    break;
+  default:
+    break;
+  }
+}
+
+/*
+ * proc_answer - process name server reply
+ * build a hostent struct in the passed request
+ */
+static int proc_answer(struct ResRequest* request, HEADER* header,
+                       u_char* buf, u_char* eob)
+{
+  char   hostbuf[HOSTLEN + 1]; /* working buffer */
+  u_char* current;             /* current position in buf */
+  char** alias;                /* alias list */
+  char** addr;                 /* address list */
+  char** base_addr;            /* original pointer to address list */
+  char*  name;                 /* pointer to name string */
+  char*  address;              /* pointer to address */
+  char*  base_address;         /* original pointer to address */
+  char*  endp;                 /* end of our buffer */
+  int    query_class;          /* answer class */
+  int    type;                 /* answer type */
+  int    rd_length;            /* record data length */
+  int    answer_count = 0;     /* answer counter */
+  int    n;                    /* temp count */
+  int    addr_count  = 0;      /* number of addresses in hostent */
+  int    alias_count = 0;      /* number of aliases in hostent */
+  int    t_ptr_seen = 0;       /* Seen a T_PTR in proc_answer? */
+  struct hostent* hp;          /* hostent getting filled */
+
+  assert(0 != request);
+  assert(0 != header);
+  assert(0 != buf);
+  assert(0 != eob);
+  
+  current = buf + sizeof(HEADER);
+  hp = &(request->he.h);
+  /*
+   * lazy allocation of request->he.buf, we don't allocate a buffer
+   * unless there is something to put in it.
+   */
+  if (!request->he.buf) {
+    request->he.buf = (char*) MyMalloc(MAXGETHOSTLEN + 1);
+    request->he.buf[MAXGETHOSTLEN] = '\0';
+    /*
+     * array of alias list pointers starts at beginning of buf
+     */
+    hp->h_aliases = (char**) request->he.buf;
+    hp->h_aliases[0] = NULL;
+    /*
+     * array of address list pointers starts after alias list pointers
+     * the actual addresses follow the the address list pointers
+     */ 
+    hp->h_addr_list = (char**)(request->he.buf + ALIAS_BLEN);
+    /*
+     * don't copy the host address to the beginning of h_addr_list
+     */
+    hp->h_addr_list[0] = NULL;
+  }
+  endp = request->he.buf + MAXGETHOSTLEN;
+  /*
+   * find the end of the address list
+   */
+  addr = hp->h_addr_list;
+  while (*addr) {
+    ++addr;
+    ++addr_count;
+  }
+  base_addr = addr;
+  /*
+   * make address point to first available address slot
+   */
+  address = request->he.buf + ADDRS_OFFSET +
+                    (sizeof(struct in_addr) * addr_count);
+  base_address = address;
+
+  /*
+   * find the end of the alias list
+   */
+  alias = hp->h_aliases;
+  while (*alias) {
+    ++alias;
+    ++alias_count;
+  }
+  /*
+   * make name point to first available space in request->buf
+   */
+  if (alias_count > 0) {
+    name = hp->h_aliases[alias_count - 1];
+    name += (strlen(name) + 1);
+  }
+  else if (hp->h_name)
+    name = hp->h_name + strlen(hp->h_name) + 1;
+  else
+    name = request->he.buf + ADDRS_OFFSET + ADDRS_DLEN;
+  /*
+   * skip past queries
+   */ 
+  while (header->qdcount-- > 0) {
+    if ((n = dn_skipname(current, eob)) < 0)
+      break;
+    current += (n + QFIXEDSZ);
+  }
+  /*
+   * process each answer sent to us blech.
+   */
+  while (header->ancount-- > 0 && current < eob && name < endp) {
+    n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
+    if (n <= 0) {
+      /*
+       * no more answers left
+       */
+      return answer_count;
+    }
+    hostbuf[HOSTLEN] = '\0';
+    /* 
+     * With Address arithmetic you have to be very anal
+     * this code was not working on alpha due to that
+     * (spotted by rodder/jailbird/dianora)
+     */
+    current += (size_t) n;
+
+    if (!((current + ANSWER_FIXED_SIZE) < eob))
+      break;
+
+    type = _getshort(current);
+    current += TYPE_SIZE;
+
+    query_class = _getshort(current);
+    current += CLASS_SIZE;
+
+    request->ttl = _getlong(current);
+    current += TTL_SIZE;
+
+    rd_length = _getshort(current);
+    current += RDLENGTH_SIZE;
+
+    /* 
+     * Wait to set request->type until we verify this structure 
+     */
+    switch(type) {
+    case T_A:
+      /*
+       * check for invalid rd_length or too many addresses
+       * ignore T_A relies if looking for a T_PTR
+       */
+      if (t_ptr_seen)
+       return answer_count;
+      if (rd_length != sizeof(struct in_addr))
+        return answer_count;
+      if (++addr_count < RES_MAXADDRS) {
+        if (answer_count == 1)
+          hp->h_addrtype = (query_class == C_IN) ?  AF_INET : AF_UNSPEC;
+
+        memcpy(address, current, sizeof(struct in_addr));
+        *addr++ = address;
+        *addr = 0;
+        address += sizeof(struct in_addr);
+
+        if (!hp->h_name) {
+          strcpy(name, hostbuf);
+          hp->h_name = name;
+          name += strlen(name) + 1;
+        }
+        Debug((DEBUG_DNS, "Resolver: A %s for %s", 
+               ircd_ntoa((char*) hp->h_addr_list[addr_count - 1]), hostbuf));
+      }
+      current += rd_length;
+      ++answer_count;
+      break;
+    case T_PTR:
+      t_ptr_seen = 1;
+      addr_count = 0;
+      addr = base_addr;
+      *addr = 0;
+      address = base_address;
+      n = dn_expand(buf, eob, current, hostbuf, sizeof(hostbuf));
+      if (n < 0) {
+        /*
+         * broken message
+         */
+        return 0;
+      }
+      else if (n == 0) {
+        /*
+         * no more answers left
+         */
+        return answer_count;
+      }
+      /*
+       * This comment is based on analysis by Shadowfax, Wohali and johan, 
+       * not me.  (Dianora) I am only commenting it.
+       *
+       * dn_expand is guaranteed to not return more than sizeof(hostbuf)
+       * but do all implementations of dn_expand also guarantee
+       * buffer is terminated with null byte? Lets not take chances.
+       *  -Dianora
+       */
+      hostbuf[HOSTLEN] = '\0';
+      current += (size_t) n;
+
+      Debug((DEBUG_DNS, "Resolver: PTR %s", hostbuf));
+      /*
+       * copy the returned hostname into the host name
+       * ignore duplicate ptr records
+       */
+      if (!hp->h_name) {
+        strcpy(name, hostbuf);
+        hp->h_name = name;
+        name += strlen(name) + 1;
+      }
+      ++answer_count;
+      break;
+    case T_CNAME:
+      Debug((DEBUG_DNS, "Resolver: CNAME %s", hostbuf));
+      if (++alias_count < RES_MAXALIASES) {
+        ircd_strncpy(name, hostbuf, endp - name);
+        *alias++ = name;
+        *alias   = 0;
+        name += strlen(name) + 1;
+      }
+      current += rd_length;
+      ++answer_count;
+      break;
+    default :
+      Debug((DEBUG_DNS,"Resolver: proc_answer type: %d for: %s", type, hostbuf));
+      break;
+    }
+  }
+  return answer_count;
+}
+
+/*
+ * resolver_read - read a dns reply from the nameserver and process it.
+ * return 0 if nothing was read from the socket, otherwise return 1
+ */
+int resolver_read(void)
+{
+  u_char             buf[sizeof(HEADER) + MAXPACKET];
+  HEADER*            header       = 0;
+  struct ResRequest* request      = 0;
+  struct CacheEntry* cp           = 0;
+  unsigned int       rc           = 0;
+  int                answer_count = 0;
+  struct sockaddr_in sin;
+
+  Debug((DEBUG_DNS, "Resolver: read"));
+  if (IO_SUCCESS != os_recvfrom_nonb(ResolverFileDescriptor,
+                                     (char*) buf, sizeof(buf), &rc, &sin)) {
+    return 0;
+  }
+  if (rc < sizeof(HEADER)) {
+    Debug((DEBUG_DNS, "Resolver: short reply %d: %s", rc, 
+           (strerror(errno)) ? strerror(errno) : "Unknown"));
+    return 0;
+  }
+  /*
+   * convert DNS reply reader from Network byte order to CPU byte order.
+   */
+  header = (HEADER*) buf;
+  /* header->id = ntohs(header->id); */
+  header->ancount = ntohs(header->ancount);
+  header->qdcount = ntohs(header->qdcount);
+  header->nscount = ntohs(header->nscount);
+  header->arcount = ntohs(header->arcount);
+  ++reinfo.re_replies;
+  /*
+   * response for an id which we have already received an answer for
+   * just ignore this response.
+   */
+  if (0 == (request = find_id(header->id))) {
+    Debug((DEBUG_DNS, "Resolver: can't find request id: %d", header->id));
+    return 1;
+  }
+  /*
+   * check against possibly fake replies
+   */
+  if (!res_ourserver(&_res, &sin)) {
+    Debug((DEBUG_DNS, "Resolver: fake reply from: %s", (const char*) &sin.sin_addr));
+    ++reinfo.re_unkrep;
+    return 1;
+  }
+
+  if ((header->rcode != NOERROR) || (header->ancount == 0)) {
+    ++reinfo.re_errors;
+    if (SERVFAIL == header->rcode)
+      resend_query(request);
+    else {
+      /*
+       * If a bad error was returned, we stop here and dont send
+       * send any more (no retries granted).
+       * Isomer: Perhaps we should return these error messages back to
+       *         the client?
+       */
+#ifdef DEBUGMODE
+      switch (header->rcode) {
+        case NOERROR:
+          Debug((DEBUG_DNS, "Fatal DNS error: No Error"));
+          break;
+        case FORMERR:
+          Debug((DEBUG_DNS, "Fatal DNS error: Format Error"));
+          break;
+        case SERVFAIL:
+          Debug((DEBUG_DNS, "Fatal DNS error: Server Failure"));
+          break;
+        case NXDOMAIN:
+          Debug((DEBUG_DNS, "DNS error: Non Existant Domain"));
+          break;
+        case NOTIMP:
+          Debug((DEBUG_DNS, "Fatal DNS error: Not Implemented"));
+          break;
+        case REFUSED:
+          Debug((DEBUG_DNS, "Fatal DNS error: Query Refused"));
+          break;
+        default:
+          Debug((DEBUG_DNS, "Unassigned fatal DNS error: %i", header->rcode));
+          break;
+      }
+#endif /* DEBUGMODE */
+      (*request->query.callback)(request->query.vptr, 0);
+      rem_request(request);
+    } 
+    return 1;
+  }
+  /*
+   * If this fails there was an error decoding the received packet, 
+   * try it again and hope it works the next time.
+   */
+  answer_count = proc_answer(request, header, buf, buf + rc);
+  if (answer_count) {
+    if (T_PTR == request->type) {
+      struct DNSReply* reply = 0;
+      if (0 == request->he.h.h_name) {
+        /*
+         * got a PTR response with no name, something bogus is happening
+         * don't bother trying again, the client address doesn't resolve 
+         */
+        (*request->query.callback)(request->query.vptr, reply);
+        rem_request(request); 
+        return 1;
+      }
+      Debug((DEBUG_DNS, "relookup %s <-> %s",
+             request->he.h.h_name, ircd_ntoa((char*) &request->addr)));
+      /*
+       * Lookup the 'authoritive' name that we were given for the
+       * ip#.  By using this call rather than regenerating the
+       * type we automatically gain the use of the cache with no
+       * extra kludges.
+       */
+      reply = gethost_byname(request->he.h.h_name, &request->query);
+      if (reply) {
+        (*request->query.callback)(request->query.vptr, reply);
+      }
+      else {
+        /*
+         * If name wasn't found, a request has been queued and it will
+         * be the last one queued.  This is rather nasty way to keep
+         * a host alias with the query. -avalon
+         */
+        MyFree(requestListTail->he.buf);
+        requestListTail->he.buf = request->he.buf;
+        request->he.buf = 0;
+        memcpy(&requestListTail->he.h, &request->he.h, sizeof(struct hostent));
+      }
+      rem_request(request);
+    }
+    else {
+      /*
+       * got a name and address response, client resolved
+       * XXX - Bug found here by Dianora -
+       * make_cache() occasionally returns a NULL pointer when a
+       * PTR returned a CNAME, cp was not checked before so the
+       * callback was being called with a value of 0x2C != NULL.
+       */
+      struct DNSReply* reply = 0;
+      if (validate_hostent(&request->he.h)) {
+        if ((cp = make_cache(request)))
+          reply = &cp->reply;
+      }
+      (*request->query.callback)(request->query.vptr, reply);
+      rem_request(request);
+    }
+  }
+  else if (!request->sent) {
+    /*
+     * XXX - we got a response for a query we didn't send with a valid id?
+     * this should never happen, bail here and leave the client unresolved
+     */
+    (*request->query.callback)(request->query.vptr, 0);
+    rem_request(request);
+  }
+  return 1;
+}
+
+/*
+ * resolver_read_multiple - process up to count reads
+ */
+void resolver_read_multiple(int count)
+{
+  int i = 0;
+  for ( ; i < count; ++i) {
+    if (0 == resolver_read())
+      return;
+  }
+}
+
+static size_t calc_hostent_buffer_size(const struct hostent* hp)
+{
+  char** p;
+  size_t count = 0;
+  assert(0 != hp);
+
+  /*
+   * space for name
+   */
+  count += (strlen(hp->h_name) + 1);
+  /*
+   * space for aliases
+   */
+  for (p = hp->h_aliases; *p; ++p)
+    count += (strlen(*p) + 1 + sizeof(char*));
+  /*
+   * space for addresses
+   */
+  for (p = hp->h_addr_list; *p; ++p)
+    count += (hp->h_length + sizeof(char*));
+  /*
+   * space for 2 nulls to terminate h_aliases and h_addr_list 
+   */
+  count += (2 * sizeof(char*));
+  return count;
+}
+
+
+/*
+ * dup_hostent - Duplicate a hostent struct, allocate only enough memory for
+ * the data we're putting in it.
+ */
+static void dup_hostent(struct Hostent* new_hp, struct hostent* hp)
+{
+  char*  p;
+  char** ap;
+  char** pp;
+  int    alias_count = 0;
+  int    addr_count = 0;
+  size_t bytes_needed = 0;
+
+  assert(0 != new_hp);
+  assert(0 != hp);
+
+  /* how much buffer do we need? */
+  bytes_needed += (strlen(hp->h_name) + 1);
+
+  pp = hp->h_aliases;
+  while (*pp) {
+    bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
+    ++alias_count;
+  }
+  pp = hp->h_addr_list;
+  while (*pp++) {
+    bytes_needed += (hp->h_length + sizeof(char*));
+    ++addr_count;
+  }
+  /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
+  bytes_needed += (2 * sizeof(char*));
+
+  /* Allocate memory */
+  new_hp->buf = (char*) MyMalloc(bytes_needed);
+
+  new_hp->h.h_addrtype = hp->h_addrtype;
+  new_hp->h.h_length = hp->h_length;
+
+  /* first write the address list */
+  pp = hp->h_addr_list;
+  ap = new_hp->h.h_addr_list =
+      (char**)(new_hp->buf + ((alias_count + 1) * sizeof(char*)));
+  p = (char*)ap + ((addr_count + 1) * sizeof(char*));
+  while (*pp)
+  {
+    *ap++ = p;
+    memcpy(p, *pp++, hp->h_length);
+    p += hp->h_length;
+  }
+  *ap = 0;
+  /* next write the name */
+  new_hp->h.h_name = p;
+  strcpy(p, hp->h_name);
+  p += (strlen(p) + 1);
+
+  /* last write the alias list */
+  pp = hp->h_aliases;
+  ap = new_hp->h.h_aliases = (char**) new_hp->buf;
+  while (*pp) {
+    *ap++ = p;
+    strcpy(p, *pp++);
+    p += (strlen(p) + 1);
+  }
+  *ap = 0;
+}
+
+/*
+ * update_hostent - Add records to a Hostent struct in place.
+ */
+static void update_hostent(struct Hostent* hp, char** addr, char** alias)
+{
+  char*  p;
+  char** ap;
+  char** pp;
+  int    alias_count = 0;
+  int    addr_count = 0;
+  char*  buf = NULL;
+  size_t bytes_needed = 0;
+
+  if (!hp || !hp->buf)
+    return;
+
+  /* how much buffer do we need? */
+  bytes_needed = strlen(hp->h.h_name) + 1;
+  pp = hp->h.h_aliases;
+  while (*pp) {
+    bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
+    ++alias_count;
+  }
+  if (alias) {
+    pp = alias;
+    while (*pp) {
+      bytes_needed += (strlen(*pp++) + 1 + sizeof(char*));
+      ++alias_count;
+    }
+  }
+  pp = hp->h.h_addr_list;
+  while (*pp++) {
+    bytes_needed += (hp->h.h_length + sizeof(char*));
+    ++addr_count;
+  }
+  if (addr) {
+    pp = addr;
+    while (*pp++) {
+      bytes_needed += (hp->h.h_length + sizeof(char*));
+      ++addr_count;
+    }
+  }
+  /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
+  bytes_needed += 2 * sizeof(char*);
+
+  /* Allocate memory */
+  buf = (char*) MyMalloc(bytes_needed);
+  assert(0 != buf);
+
+  /* first write the address list */
+  pp = hp->h.h_addr_list;
+  ap = hp->h.h_addr_list =
+      (char**)(buf + ((alias_count + 1) * sizeof(char*)));
+  p = (char*)ap + ((addr_count + 1) * sizeof(char*));
+  while (*pp) {
+    memcpy(p, *pp++, hp->h.h_length);
+    *ap++ = p;
+    p += hp->h.h_length;
+  }
+  if (addr) {
+    while (*addr) {
+      memcpy(p, *addr++, hp->h.h_length);
+      *ap++ = p;
+      p += hp->h.h_length;
+    }
+  }
+  *ap = 0;
+
+  /* next write the name */
+  strcpy(p, hp->h.h_name);
+  hp->h.h_name = p;
+  p += (strlen(p) + 1);
+
+  /* last write the alias list */
+  pp = hp->h.h_aliases;
+  ap = hp->h.h_aliases = (char**) buf;
+  while (*pp) {
+    strcpy(p, *pp++);
+    *ap++ = p;
+    p += (strlen(p) + 1);
+  }
+  if (alias) {
+    while (*alias) {
+      strcpy(p, *alias++);
+      *ap++ = p;
+      p += (strlen(p) + 1);
+    }
+  }
+  *ap = 0;
+  /* release the old buffer */
+  p = hp->buf;
+  hp->buf = buf;
+  MyFree(p);
+}
+
+/*
+ * hash_number - IP address hash function
+ */
+static int hash_number(const unsigned char* ip)
+{
+  /* could use loop but slower */
+  unsigned int hashv;
+  const u_char* p = (const u_char*) ip;
+
+  assert(0 != p);
+
+  hashv = *p++;
+  hashv += hashv + *p++;
+  hashv += hashv + *p++;
+  hashv += hashv + *p;
+  hashv %= ARES_CACSIZE;
+  return hashv;
+}
+
+/*
+ * hash_name - hostname hash function
+ */
+static int hash_name(const char* name)
+{
+  unsigned int hashv = 0;
+  const u_char* p = (const u_char*) name;
+
+  assert(0 != p);
+
+  for (; *p && *p != '.'; ++p)
+    hashv += *p;
+  hashv %= ARES_CACSIZE;
+  return hashv;
+}
+
+/*
+ * add_to_cache - Add a new cache item to the queue and hash table.
+ */
+static struct CacheEntry* add_to_cache(struct CacheEntry* ocp)
+{
+  int  hashv;
+
+  assert(0 != ocp);
+
+  ocp->list_next = cacheTop;
+  cacheTop = ocp;
+
+  hashv = hash_name(ocp->he.h.h_name);
+
+  ocp->hname_next = hashtable[hashv].name_list;
+  hashtable[hashv].name_list = ocp;
+
+  hashv = hash_number((const unsigned char*) ocp->he.h.h_addr);
+
+  ocp->hnum_next = hashtable[hashv].num_list;
+  hashtable[hashv].num_list = ocp;
+
+  /*
+   * LRU deletion of excessive cache entries.
+   */
+  if (++cachedCount > MAXCACHED) {
+    struct CacheEntry* cp;
+    struct CacheEntry* cp_next;
+    for (cp = ocp->list_next; cp; cp = cp_next) {
+      cp_next = cp->list_next;
+      rem_cache(cp);
+    }
+  }
+  ++cainfo.ca_adds;
+  return ocp;
+}
+
+/*
+ * update_list - does not alter the cache structure passed. It is assumed that
+ * it already contains the correct expire time, if it is a new entry. Old
+ * entries have the expirey time updated.
+*/
+static void update_list(struct ResRequest* request, struct CacheEntry* cachep)
+{
+  struct CacheEntry*  cp = cachep;
+  char*    s;
+  char*    t;
+  int      i;
+  int      j;
+  char**   ap;
+  char*    addrs[RES_MAXADDRS + 1];
+  char*    aliases[RES_MAXALIASES + 1];
+
+  /*
+   * search for the new cache item in the cache list by hostname.
+   * If found, move the entry to the top of the list and return.
+   */
+  ++cainfo.ca_updates;
+
+  if (!request)
+    return;
+  /*
+   * Compare the cache entry against the new record.  Add any
+   * previously missing names for this entry.
+   */
+  *aliases = 0;
+  ap = aliases;
+  for (i = 0, s = request->he.h.h_name; s; s = request->he.h.h_aliases[i++]) {
+    for (j = 0, t = cp->he.h.h_name; t; t = cp->he.h.h_aliases[j++]) {
+      if (0 == ircd_strcmp(t, s))
+        break;
+    }
+    if (!t) {
+      *ap++ = s;
+      *ap = 0;
+    }
+  }
+  /*
+   * Do the same again for IP#'s.
+   */
+  *addrs = 0;
+  ap = addrs;
+  for (i = 0; (s = request->he.h.h_addr_list[i]); i++) {
+    for (j = 0; (t = cp->he.h.h_addr_list[j]); j++) {
+      if (!memcmp(t, s, sizeof(struct in_addr)))
+        break;
+    }
+    if (!t) {
+      *ap++ = s;
+      *ap = 0;
+    }
+  }
+  if (*addrs || *aliases)
+    update_hostent(&cp->he, addrs, aliases);
+}
+
+/*
+ * find_cache_name - find name in nameserver cache
+ */
+static struct CacheEntry* find_cache_name(const char* name)
+{
+  struct CacheEntry* cp;
+  char*   s;
+  int     hashv;
+  int     i;
+
+  assert(0 != name);
+  hashv = hash_name(name);
+
+  cp = hashtable[hashv].name_list;
+
+  for (; cp; cp = cp->hname_next) {
+    for (i = 0, s = cp->he.h.h_name; s; s = cp->he.h.h_aliases[i++]) {
+      if (0 == ircd_strcmp(s, name)) {
+        ++cainfo.ca_na_hits;
+        return cp;
+      }
+    }
+  }
+
+  for (cp = cacheTop; cp; cp = cp->list_next) {
+    /*
+     * if no aliases or the hash value matches, we've already
+     * done this entry and all possiblilities concerning it.
+     */
+    if (!cp->he.h.h_name || hashv == hash_name(cp->he.h.h_name))
+      continue;
+    for (i = 0, s = cp->he.h.h_aliases[i]; s; s = cp->he.h.h_aliases[++i]) {
+      if (0 == ircd_strcmp(name, s)) {
+        ++cainfo.ca_na_hits;
+        return cp;
+      }
+    }
+  }
+  return NULL;
+}
+
+/*
+ * find_cache_number - find a cache entry by ip# and update its expire time
+ */
+static struct CacheEntry* find_cache_number(struct ResRequest* request,
+                                            const char* addr)
+{
+  struct CacheEntry* cp;
+  int     hashv;
+  int     i;
+
+  assert(0 != addr);
+  hashv = hash_number((const unsigned char*) addr);
+  cp = hashtable[hashv].num_list;
+
+  for (; cp; cp = cp->hnum_next) {
+    for (i = 0; cp->he.h.h_addr_list[i]; ++i) {
+      if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
+        ++cainfo.ca_nu_hits;
+        return cp;
+      }
+    }
+  }
+  for (cp = cacheTop; cp; cp = cp->list_next) {
+    /*
+     * single address entry...would have been done by hashed
+     * search above...
+     * if the first IP# has the same hashnumber as the IP# we
+     * are looking for, its been done already.
+     */
+    if (!cp->he.h.h_addr_list[1] || 
+        hashv == hash_number((const unsigned char*) cp->he.h.h_addr_list[0]))
+      continue;
+    for (i = 1; cp->he.h.h_addr_list[i]; ++i) {
+      if (!memcmp(cp->he.h.h_addr_list[i], addr, sizeof(struct in_addr))) {
+        ++cainfo.ca_nu_hits;
+        return cp;
+      }
+    }
+  }
+  return NULL;
+}
+
+static struct CacheEntry* make_cache(struct ResRequest* request)
+{
+  struct CacheEntry* cp;
+  int     i;
+  struct hostent* hp;
+  assert(0 != request);
+
+  hp = &request->he.h;
+  /*
+   * shouldn't happen but it just might...
+   */
+  assert(0 != hp->h_name);
+/*    assert(0 != hp->h_addr_list[0]); */
+  if (!hp->h_name || !hp->h_addr_list[0])
+    return NULL;
+  /*
+   * Make cache entry.  First check to see if the cache already exists
+   * and if so, return a pointer to it.
+   */
+  for (i = 0; hp->h_addr_list[i]; ++i) {
+    if ((cp = find_cache_number(request, hp->h_addr_list[i]))) {
+      update_list(request, cp);
+      return cp;
+    }
+  }
+  /*
+   * a matching entry wasnt found in the cache so go and make one up.
+   */ 
+  cp = (struct CacheEntry*) MyMalloc(sizeof(struct CacheEntry));
+  assert(0 != cp);
+
+  memset(cp, 0, sizeof(struct CacheEntry));
+  dup_hostent(&cp->he, hp);
+  cp->reply.hp = &cp->he.h;
+  /*
+   * hmmm... we could time out the cache after 10 minutes regardless
+   * would that be reasonable since we don't save the reply?
+   */ 
+  if (request->ttl < AR_TTL) {
+    ++reinfo.re_shortttl;
+    cp->ttl = AR_TTL;
+  }
+  else
+    cp->ttl = request->ttl;
+  cp->expireat = CurrentTime + cp->ttl;
+  return add_to_cache(cp);
+}
+
+/*
+ * rem_cache - delete a cache entry from the cache structures 
+ * and lists and return all memory used for the cache back to the memory pool.
+ */
+static void rem_cache(struct CacheEntry* ocp)
+{
+  struct CacheEntry** cp;
+  int                 hashv;
+  struct hostent*     hp;
+  assert(0 != ocp);
+
+
+  if (0 < ocp->reply.ref_count) {
+    if (ocp->expireat < CurrentTime) {
+      ocp->expireat = CurrentTime + AR_TTL;
+      Debug((DEBUG_DNS, "Resolver: referenced cache entry not removed for: %s",
+            ocp->he.h.h_name));
+    }
+    return;
+  }
+  /*
+   * remove cache entry from linked list
+   */
+  for (cp = &cacheTop; *cp; cp = &((*cp)->list_next)) {
+    if (*cp == ocp) {
+      *cp = ocp->list_next;
+      break;
+    }
+  }
+  hp = &ocp->he.h;
+  /*
+   * remove cache entry from hashed name list
+   */
+  assert(0 != hp->h_name);
+  hashv = hash_name(hp->h_name);
+
+  for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next)) {
+    if (*cp == ocp) {
+      *cp = ocp->hname_next;
+      break;
+    }
+  }
+  /*
+   * remove cache entry from hashed number list
+   */
+  hashv = hash_number((const unsigned char*) hp->h_addr);
+  assert(-1 < hashv);
+
+  for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next)) {
+    if (*cp == ocp) {
+      *cp = ocp->hnum_next;
+      break;
+    }
+  }
+  /*
+   * free memory used to hold the various host names and the array
+   * of alias pointers.
+   */
+  MyFree(ocp->he.buf);
+  MyFree(ocp);
+  --cachedCount;
+  ++cainfo.ca_dels;
+}
+
+void flush_resolver_cache(void)
+{
+  /*
+   * stubbed - iterate cache and remove everything that isn't referenced
+   */
+}
+
+/*
+ * m_dns - dns status query
+ */
+int m_dns(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
+{
+#if !defined(NDEBUG)
+  struct CacheEntry* cp;
+  int     i;
+  struct hostent* hp;
+
+  if (parv[1] && *parv[1] == 'l') {
+    for(cp = cacheTop; cp; cp = cp->list_next) {
+      hp = &cp->he.h;
+      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Ex %d ttl %d host %s(%s)",
+                   sptr, cp->expireat - CurrentTime, cp->ttl,
+                   hp->h_name, ircd_ntoa(hp->h_addr));
+      for (i = 0; hp->h_aliases[i]; i++)
+        sendcmdto_one(&me, CMD_NOTICE, sptr, "%C : %s = %s (CN)", sptr,
+                     hp->h_name, hp->h_aliases[i]);
+      for (i = 1; hp->h_addr_list[i]; i++)
+        sendcmdto_one(&me, CMD_NOTICE, sptr, "%C : %s = %s (IP)", sptr,
+                     hp->h_name, ircd_ntoa(hp->h_addr_list[i]));
+    }
+    return 0;
+  }
+  if (parv[1] && *parv[1] == 'd') {
+    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :ResolverFileDescriptor = %d", 
+                 sptr, ResolverFileDescriptor);
+    return 0;
+  }
+  sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ca %d Cd %d Ce %d Cl %d Ch %d:%d "
+               "Cu %d", sptr,
+               cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires,
+               cainfo.ca_lookups, cainfo.ca_na_hits, cainfo.ca_nu_hits, 
+               cainfo.ca_updates);
+  
+  sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Re %d Rl %d/%d Rp %d Rq %d",
+               sptr, reinfo.re_errors, reinfo.re_nu_look,
+               reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
+  sendcmdto_one(&me, CMD_NOTICE, sptr,"%C :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr,
+               reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
+               reinfo.re_resends, reinfo.re_timeouts);
+#endif
+  return 0;
+}
+
+size_t cres_mem(struct Client* sptr)
+{
+  struct CacheEntry* entry;
+  struct ResRequest* request;
+  size_t cache_mem     = 0;
+  size_t request_mem   = 0;
+  int    cache_count   = 0;
+  int    request_count = 0;
+
+  for (entry = cacheTop; entry; entry = entry->list_next) {
+    cache_mem += sizeof(struct CacheEntry);
+    cache_mem += calc_hostent_buffer_size(&entry->he.h); 
+    ++cache_count;
+  }
+  for (request = requestListHead; request; request = request->next) {
+    request_mem += sizeof(struct ResRequest);
+    if (request->name)
+      request_mem += strlen(request->name) + 1; 
+    if (request->he.buf)
+      request_mem += MAXGETHOSTLEN + 1;
+    ++request_count;
+  }
+
+  if (cachedCount != cache_count) {
+    send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
+              ":Resolver: cache count mismatch: %d != %d", cachedCount,
+              cache_count);
+    assert(cachedCount == cache_count);
+  }
+  send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG,
+            ":Resolver: cache %d(%d) requests %d(%d)", cache_count,
+            cache_mem, request_count, request_mem);
+  return cache_mem + request_mem;
+}
+