]> git.mxchange.org Git - simgear.git/commitdiff
Initial commit for a DNS service resolver
authorTorsten Dreyer <torsten@t3r.de>
Thu, 28 Apr 2016 06:19:30 +0000 (08:19 +0200)
committerTorsten Dreyer <torsten@t3r.de>
Thu, 28 Apr 2016 07:37:08 +0000 (09:37 +0200)
- import udns library
  (http://www.corpit.ru/mjt/udns.html)
- initial draft for a DNSClient (derived from HTTPClient)

Enable compile and test by adding -D ENABLE_DNS=Yes to cmake flags

42 files changed:
3rdparty/CMakeLists.txt
3rdparty/udns/CMakeLists.txt [new file with mode: 0644]
3rdparty/udns/COPYING.LGPL [new file with mode: 0644]
3rdparty/udns/Makefile.in [new file with mode: 0644]
3rdparty/udns/NEWS [new file with mode: 0644]
3rdparty/udns/NOTES [new file with mode: 0644]
3rdparty/udns/TODO [new file with mode: 0644]
3rdparty/udns/config.h.in [new file with mode: 0644]
3rdparty/udns/configure [new file with mode: 0755]
3rdparty/udns/configure.lib [new file with mode: 0644]
3rdparty/udns/dnsget.1 [new file with mode: 0644]
3rdparty/udns/dnsget.c [new file with mode: 0644]
3rdparty/udns/ex-rdns.c [new file with mode: 0644]
3rdparty/udns/getopt.c [new file with mode: 0644]
3rdparty/udns/inet_XtoX.c [new file with mode: 0644]
3rdparty/udns/rblcheck.1 [new file with mode: 0644]
3rdparty/udns/rblcheck.c [new file with mode: 0644]
3rdparty/udns/udns.3 [new file with mode: 0644]
3rdparty/udns/udns.h [new file with mode: 0644]
3rdparty/udns/udns_XtoX.c [new file with mode: 0644]
3rdparty/udns/udns_bl.c [new file with mode: 0644]
3rdparty/udns/udns_codes.c [new file with mode: 0644]
3rdparty/udns/udns_dn.c [new file with mode: 0644]
3rdparty/udns/udns_dntosp.c [new file with mode: 0644]
3rdparty/udns/udns_init.c [new file with mode: 0644]
3rdparty/udns/udns_jran.c [new file with mode: 0644]
3rdparty/udns/udns_misc.c [new file with mode: 0644]
3rdparty/udns/udns_parse.c [new file with mode: 0644]
3rdparty/udns/udns_resolver.c [new file with mode: 0644]
3rdparty/udns/udns_rr_a.c [new file with mode: 0644]
3rdparty/udns/udns_rr_mx.c [new file with mode: 0644]
3rdparty/udns/udns_rr_naptr.c [new file with mode: 0644]
3rdparty/udns/udns_rr_ptr.c [new file with mode: 0644]
3rdparty/udns/udns_rr_srv.c [new file with mode: 0644]
3rdparty/udns/udns_rr_txt.c [new file with mode: 0644]
CMakeLists.txt
simgear/io/CMakeLists.txt
simgear/io/DNSClient.cxx [new file with mode: 0644]
simgear/io/DNSClient.hxx [new file with mode: 0644]
simgear/io/test_DNS.cxx [new file with mode: 0644]
simgear/io/test_DNS.hxx [new file with mode: 0644]
simgear/io/text_DNS.cxx [new file with mode: 0644]

index 5c6fbf53f27afa2679641707fd5b01498241376c..e9b52823822c6a3c894d397982879c07ea2accb4 100644 (file)
@@ -3,3 +3,7 @@ if (NOT SYSTEM_EXPAT)
 endif()
 
 add_subdirectory(utf8)
+
+if (ENABLE_DNS)
+       add_subdirectory(udns)
+endif()
diff --git a/3rdparty/udns/CMakeLists.txt b/3rdparty/udns/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2bccfb9
--- /dev/null
@@ -0,0 +1,39 @@
+include (SimGearComponent)
+INCLUDE (CheckFunctionExists)
+INCLUDE (CheckSymbolExists) 
+
+CHECK_FUNCTION_EXISTS(poll HAVE_POLL)
+CHECK_FUNCTION_EXISTS(getopt HAVE_GETOPT)
+CHECK_FUNCTION_EXISTS(inet_ntop HAVE_INET_PTON_NTOP)
+CHECK_SYMBOL_EXISTS(AF_INET6 "netinet/in.h" HAVE_IPv6) 
+
+CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_BINARY_DIR}/config.h)
+include_directories(${CMAKE_BINARY_DIR})
+
+set(HEADERS
+       config.h
+)
+
+set(SOURCES
+       udns_resolver.c
+       udns_dn.c 
+       udns_dntosp.c 
+       udns_parse.c 
+       udns_resolver.c 
+       udns_init.c
+    udns_misc.c 
+       udns_XtoX.c
+    udns_rr_a.c 
+       udns_rr_ptr.c 
+       udns_rr_mx.c 
+       udns_rr_txt.c 
+       udns_bl.c
+    udns_rr_srv.c 
+       udns_rr_naptr.c 
+       udns_codes.c 
+       udns_jran.c
+)
+
+simgear_component(udns 3rdparty/udns "${SOURCES}" "${HEADERS}")
+#simgear_component(utf8-internal 3rdparty/utf8/utf8 "" "${HEADERS_utf8}")
+
diff --git a/3rdparty/udns/COPYING.LGPL b/3rdparty/udns/COPYING.LGPL
new file mode 100644 (file)
index 0000000..4362b49
--- /dev/null
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, 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 library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+  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.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+  If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be 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.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+  9. 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 Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+\f
+  11. 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 Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  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 library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/3rdparty/udns/Makefile.in b/3rdparty/udns/Makefile.in
new file mode 100644 (file)
index 0000000..ec08520
--- /dev/null
@@ -0,0 +1,197 @@
+#! /usr/bin/make -rf
+# Makefile.in
+# libudns Makefile
+#
+# Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+# This file is part of UDNS library, an async DNS stub resolver.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library, in file named COPYING.LGPL; if not,
+# write to the Free Software Foundation, Inc., 59 Temple Place,
+# Suite 330, Boston, MA  02111-1307  USA
+
+NAME   = udns
+VERS   = 0.4
+SOVER  = 0
+
+SRCS   = udns_dn.c udns_dntosp.c udns_parse.c udns_resolver.c udns_init.c \
+       udns_misc.c udns_XtoX.c \
+       udns_rr_a.c udns_rr_ptr.c udns_rr_mx.c udns_rr_txt.c udns_bl.c \
+       udns_rr_srv.c udns_rr_naptr.c udns_codes.c udns_jran.c
+USRCS  = dnsget.c rblcheck.c ex-rdns.c
+DIST   = COPYING.LGPL udns.h udns.3 dnsget.1 rblcheck.1 $(SRCS) $(USRCS) \
+        NEWS TODO NOTES Makefile.in configure configure.lib \
+        inet_XtoX.c getopt.c
+
+OBJS   = $(SRCS:.c=.o) $(GEN:.c=.o)
+LIB    = lib$(NAME).a
+LIBFL  = -L. -l$(NAME)
+
+SOBJS  = $(OBJS:.o=.lo)
+SOLIB  = lib$(NAME)_s.so
+SOLIBV = lib$(NAME).so.$(SOVER)
+SOLIBFL= -L. -l$(NAME)_s
+
+UTILS   = $(USRCS:.c=)
+UOBJS   = $(USRCS:.c=.o)
+SOUTILS = $(USRCS:.c=_s)
+
+NAMEPFX = $(NAME)-$(VERS)
+
+CC = @CC@
+CFLAGS = @CFLAGS@
+CDEFS = @CDEFS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+LDSHARED = $(LD) -shared
+PICFLAGS = -fPIC
+AWK = awk
+TAR = tar
+
+all: static
+
+.SUFFIXES: .c .o .lo
+
+static: $(LIB) $(UTILS)
+staticlib: $(LIB)
+$(LIB): $(OBJS)
+       -rm -f $@
+       $(AR) rv $@ $(OBJS)
+.c.o:
+       $(CC) $(CFLAGS) $(CDEFS) -c $<
+
+shared: $(SOLIBV) $(SOUTILS)
+sharedlib: $(SOLIBV)
+
+$(SOLIBV): $(SOBJS)
+       $(LDSHARED) -Wl,--soname,$(SOLIBV) -o $@ $(SOBJS) $(LDFLAGS) $(LIBS)
+$(SOLIB): $(SOLIBV)
+       rm -f $@
+       ln -s $(SOLIBV) $@
+.c.lo:
+       $(CC) $(CFLAGS) $(PICFLAGS) $(CDEFS) -o $@ -c $<
+
+# udns_codes.c is generated from udns.h
+udns_codes.c: udns.h
+       @echo Generating $@
+       @set -e; exec >$@.tmp; \
+       set T type C class R rcode; \
+       echo "/* Automatically generated. */"; \
+       echo "#include \"udns.h\""; \
+       while [ "$$1" ]; do \
+        echo; \
+        echo "const struct dns_nameval dns_$${2}tab[] = {"; \
+        $(AWK) "/^  DNS_$${1}_[A-Z0-9_]+[      ]*=/ \
+         { printf \" {%s,\\\"%s\\\"},\\n\", \$$1, substr(\$$1,7) }" \
+         udns.h ; \
+        echo " {0,0}};"; \
+        echo "const char *dns_$${2}name(enum dns_$${2} code) {"; \
+        echo " static char nm[20];"; \
+        echo " switch(code) {"; \
+        $(AWK) "BEGIN{i=0} \
+          /^  DNS_$${1}_[A-Z0-9_]+[    ]*=/ \
+          {printf \" case %s: return dns_$${2}tab[%d].name;\\n\",\$$1,i++}\
+          " udns.h ; \
+        echo " }"; \
+        echo " return _dns_format_code(nm,\"$$2\",code);"; \
+        echo "}"; \
+        shift 2; \
+       done
+       @mv $@.tmp $@
+
+udns.3.html: udns.3
+       groff -man -Thtml udns.3 > $@.tmp
+       mv $@.tmp $@
+
+dist: $(NAMEPFX).tar.gz
+$(NAMEPFX).tar.gz: $(DIST)
+       $(TAR) -cv -f $@ -z --transform 's|^|$(NAMEPFX)/|' $(DIST)
+
+subdist:
+       cp -p $(DIST) $(TARGET)/
+
+clean:
+       rm -f $(OBJS)
+       rm -f $(SOBJS)
+       rm -f $(UOBJS)
+       rm -f config.log
+distclean: clean
+       rm -f $(LIB) $(SOLIB) $(SOLIBV) udns.3.html
+       rm -f $(UTILS) $(SOUTILS)
+       rm -f config.status config.h Makefile
+
+
+Makefile: configure configure.lib Makefile.in
+       ./configure
+       @echo
+       @echo Please rerun make >&2
+       @exit 1
+
+.PHONY: all static staticlib shared sharedlib dist clean distclean subdist \
+  depend dep deps
+
+depend dep deps: $(SRCS) $(USRC)
+       @echo Generating deps for:
+       @echo \ $(SRCS)
+       @echo \ $(USRCS)
+       @sed '/^# depend/q' Makefile.in > Makefile.tmp
+       @set -e; \
+       for f in $(SRCS) $(USRCS); do \
+        echo $${f%.c}.o $${f%.c}.lo: $$f \
+         `sed -n 's/^#[        ]*include[      ]*"\(.*\)".*/\1/p' $$f`; \
+       done >> Makefile.tmp; \
+       for f in $(USRCS:.c=.o); do \
+        echo "$${f%.?}: $$f \$$(LIB)"; \
+        echo " \$$(LD) \$$(LDLAGS) -o \$$@ $$f \$$(LIBFL) \$$(LIBS)"; \
+        echo "$${f%.?}_s: $$f \$$(SOLIB)"; \
+        echo " \$$(LD) \$$(LDFLAGS) -o \$$@ $$f \$$(SOLIBFL)"; \
+       done >> Makefile.tmp ; \
+       if cmp Makefile.tmp Makefile.in >/dev/null 2>&1 ; then \
+        echo Makefile.in unchanged; rm -f Makefile.tmp; \
+       else \
+        echo Updating Makfile.in; mv -f Makefile.tmp Makefile.in ; \
+       fi
+
+# depend
+udns_dn.o udns_dn.lo: udns_dn.c udns.h
+udns_dntosp.o udns_dntosp.lo: udns_dntosp.c udns.h
+udns_parse.o udns_parse.lo: udns_parse.c udns.h
+udns_resolver.o udns_resolver.lo: udns_resolver.c config.h udns.h
+udns_init.o udns_init.lo: udns_init.c config.h udns.h
+udns_misc.o udns_misc.lo: udns_misc.c udns.h
+udns_XtoX.o udns_XtoX.lo: udns_XtoX.c config.h udns.h inet_XtoX.c
+udns_rr_a.o udns_rr_a.lo: udns_rr_a.c udns.h
+udns_rr_ptr.o udns_rr_ptr.lo: udns_rr_ptr.c udns.h
+udns_rr_mx.o udns_rr_mx.lo: udns_rr_mx.c udns.h
+udns_rr_txt.o udns_rr_txt.lo: udns_rr_txt.c udns.h
+udns_bl.o udns_bl.lo: udns_bl.c udns.h
+udns_rr_srv.o udns_rr_srv.lo: udns_rr_srv.c udns.h
+udns_rr_naptr.o udns_rr_naptr.lo: udns_rr_naptr.c udns.h
+udns_codes.o udns_codes.lo: udns_codes.c udns.h
+udns_jran.o udns_jran.lo: udns_jran.c udns.h
+dnsget.o dnsget.lo: dnsget.c config.h udns.h getopt.c
+rblcheck.o rblcheck.lo: rblcheck.c config.h udns.h getopt.c
+ex-rdns.o ex-rdns.lo: ex-rdns.c udns.h
+dnsget: dnsget.o $(LIB)
+       $(LD) $(LDLAGS) -o $@ dnsget.o $(LIBFL) $(LIBS)
+dnsget_s: dnsget.o $(SOLIB)
+       $(LD) $(LDFLAGS) -o $@ dnsget.o $(SOLIBFL)
+rblcheck: rblcheck.o $(LIB)
+       $(LD) $(LDLAGS) -o $@ rblcheck.o $(LIBFL) $(LIBS)
+rblcheck_s: rblcheck.o $(SOLIB)
+       $(LD) $(LDFLAGS) -o $@ rblcheck.o $(SOLIBFL)
+ex-rdns: ex-rdns.o $(LIB)
+       $(LD) $(LDLAGS) -o $@ ex-rdns.o $(LIBFL) $(LIBS)
+ex-rdns_s: ex-rdns.o $(SOLIB)
+       $(LD) $(LDFLAGS) -o $@ ex-rdns.o $(SOLIBFL)
diff --git a/3rdparty/udns/NEWS b/3rdparty/udns/NEWS
new file mode 100644 (file)
index 0000000..88aff6f
--- /dev/null
@@ -0,0 +1,136 @@
+NEWS
+User-visible changes in udns library.  Recent changes on top.
+
+0.4 (Jan 2014)
+
+ - bugfix: fix a bug in new list code introduced in 0.3
+ - portability: use $(LD)/$(LDFLAGS)/$(LIBS)
+
+0.3 (Jan 2014)
+
+ - bugfix: refactor double-linked list implementation in udns_resolver.c
+   (internal to the library) to be more strict-aliasing-friendly, because
+   old code were miscompiled by gcc.
+
+ - bugfix: forgotten strdup() in rblcheck
+
+0.2 (Dec 2011)
+
+ - bugfix: SRV RR handling: fix domain name parsing and crash in case
+   if no port is specified on input for SRV record query
+
+ - (trivial api) dns_set_opts() now returns number of unrecognized 
+   options instead of always returning 0
+
+ - dnsget: combine -f and -o options in dnsget (and stop documenting -f),
+   and report unknown/invalid -o options (and error out)
+
+ - dnsget: pretty-print SSHFP RRs
+
+ 0.1 (Dec 2010)
+
+ - bugfix: udns_new(old) - when actually cloning another context -
+   makes the new context referencing memory from old, which leads
+   to crashes when old is modified later
+
+ - use random queue IDs (the 16bit qID) in queries instead of sequentional
+   ones, based on simple pseudo-random RNG by Bob Jenkins (udns_jran.[ch]).
+   Some people believe that this improves security (CVE-2008-1447).  I'm
+   still not convinced (see comments in udns_resolver.c), but it isn't
+   difficult to add after all.
+
+ - deprecate dns_random16() function which was declared in udns.h
+   (not anymore) but never documented.  In order to keep ABI compatible
+   it is still exported.
+
+ - library has a way now to set query flags (DNS_SET_DO; DNS_SET_CD).
+
+ - dnsget now prints non-printable chars in all strings in DNS RRs using
+   decimal escape sequences (\%03u) instead of hexadecimal (\%02x) when
+   before - other DNS software does it like this.
+
+ - recognize a few more record types in dnsget, notable some DNSSEC RRs;
+   add -f option for dnsget to set query flags.
+
+ - udns is not a Debian native package anymore (was a wrong idea)
+
+0.0.9 (16 Jan 2007)
+
+ - incompat: minor API changes in dns_init() &friends.  dns_init()
+   now requires extra `struct dns_ctx *' argument.  Not bumped
+   soversion yet - I only expect one "release" with this change.
+
+ - many small bugfixes, here and there
+
+ - more robust FORMERR replies handling - not only such replies are now
+   recognized, but udns retries queries without EDNS0 extensions if tried
+   with, but server reported FORMERR
+
+ - portability changes, udns now includes getopt() implementation fo
+   the systems lacking it (mostly windows), and dns_ntop()&dns_pton(),
+   which are either just wrappers for system functions or reimplementations.
+
+ - build is now based on autoconf-like configuration
+
+ - NAPTR (RFC3403) RR decoding support
+
+ - new file NOTES which complements TODO somewhat, and includes some
+   important shortcomings
+
+ - many internal cleanups, including some preparations for better error
+   recovery, security and robustness (and thus API changes)
+
+ - removed some #defines which are now unused (like DNS_MAXSRCH)
+
+ - changed WIN32 to WINDOWS everywhere in preprocessor tests,
+   to be able to build it on win64 as well
+
+0.0.8 (12 Sep 2005)
+
+ - added SRV records (rfc2782) parsing,
+   thanks to Thadeu Lima de Souza Cascardo for implementation.
+
+ - bugfixes:
+   o use uninitialized value when no reply, library died with assertion:
+     assert((status < 0 && result == 0) || (status >= 0 && result != 0)).
+   o on some OSes, struct sockaddr_in has additional fields, so
+     memcmp'ing two sockaddresses does not work.
+
+ - rblcheck(.1)
+
+0.0.7 (20 Apr 2005)
+
+ - dnsget.1 manpage and several enhancements to dnsget.
+
+ - allow nameserver names for -n option of dnsget.
+
+ - API change: all dns_submit*() routines now does not expect
+   last `now' argument, since requests aren't sent immediately
+   anymore.
+
+ - API change: different application timer callback mechanism.
+   Udns now uses single per-context timer instead of per-query.
+
+ - don't assume DNS replies only contain backward DN pointers,
+   allow forward pointers too.  Change parsing API.
+
+ - debianize
+
+0.0.6 (08 Apr 2005)
+
+ - use double sorted list for requests (sorted by deadline).
+   This should significantly speed up timeout processing for
+   large number of requests.
+
+ - changed debugging interface, so it is finally useable
+   (still not documented).
+
+ - dnsget routine is now Officially Useable, and sometimes
+   even more useable than `host' from BIND distribution
+   (and sometimes not - dnsget does not have -C option
+   and TCP mode)
+
+ - Debian packaging in debian/ -- udns is now maintained as a
+   native Debian package.
+
+ - alot (and I really mean alot) of code cleanups all over.
diff --git a/3rdparty/udns/NOTES b/3rdparty/udns/NOTES
new file mode 100644 (file)
index 0000000..b99a077
--- /dev/null
@@ -0,0 +1,226 @@
+Assorted notes about udns (library).
+
+UDP-only mode
+~~~~~~~~~~~~~
+
+First of all, since udns is (currently) UDP-only, there are some
+shortcomings.
+
+It assumes that a reply will fit into a UDP buffer.  With adoption of EDNS0,
+and general robustness of IP stacks, in most cases it's not an issue.  But
+in some cases there may be problems:
+
+ - if an RRset is "very large" so it does not fit even in buffer of size
+   requested by the library (current default is 4096; some servers limits
+   it further), we will not see the reply, or will only see "damaged"
+   reply (depending on the server).
+
+ - many DNS servers ignores EDNS0 option requests.  In this case, no matter
+   which buffer size udns library will request, such servers reply is limited
+   to 512 bytes (standard pre-EDNS0 DNS packet size).  (Udns falls back to
+   non-EDNO0 query if EDNS0-enabled one received FORMERR or NOTIMPL error).
+
+The problem is that with this, udns currently will not consider replies with
+TC (truncation) bit set, and will treat such replies the same way as it
+treats SERVFAIL replies, thus trying next server, or temp-failing the query
+if no more servers to try.  In other words, if the reply is really large, or
+if the servers you're using don't support EDNS0, your application will be
+unable to resolve a given name.
+
+Yet it's not common situation - in practice, it's very rare.
+
+Implementing TCP mode isn't difficult, but it complicates API significantly.
+Currently udns uses only single UDP socket (or - maybe in the future - two,
+see below), but in case of TCP, it will need to open and close sockets for
+TCP connections left and right, and that have to be integrated into an
+application's event loop in an easy and efficient way.  Plus all the
+timeouts - different for connect(), write, and several stages of read.
+
+IPv6 vs IPv4 usage
+~~~~~~~~~~~~~~~~~~
+
+This is only relevant for nameservers reachable over IPv6, NOT for IPv6
+queries.  I.e., if you've IPv6 addresses in 'nameservers' line in your
+/etc/resolv.conf file.  Even more: if you have BOTH IPv6 AND IPv4 addresses
+there.  Or pass them to udns initialization routines.
+
+Since udns uses a single UDP socket to communicate with all nameservers,
+it should support both v4 and v6 communications.  Most current platforms
+supports this mode - using PF_INET6 socket and V4MAPPED addresses, i.e,
+"tunnelling" IPv4 inside IPv6.  But not all systems supports this.  And
+more, it has been said that such mode is deprecated.
+
+So, list only IPv4 or only IPv6 addresses, but don't mix them, in your
+/etc/resolv.conf.
+
+An alternative is to use two sockets instead of 1 - one for IPv6 and one
+for IPv4.  For now I'm not sure if it's worth the complexity - again, of
+the API, not the library itself (but this will not simplify library either).
+
+Single socket for all queries
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Using single UDP socket for sending queries to all nameservers has obvious
+advantages.  First it's, again, trivial, simple to use API.  And simple
+library too.  Also, after sending queries to all nameservers (in case first
+didn't reply in time), we will be able to receive late reply from first
+nameserver and accept it.
+
+But this mode has disadvantages too.  Most important is that it's much easier
+to send fake reply to us, as the UDP port where we expects the reply to come
+to is constant during the whole lifetime of an application.  More secure
+implementations uses random port for every single query.  While port number
+(16 bits integer) can not hold much randomness, it's still of some help.
+Ok, udns is a stub resolver, so it expects sorta friendly environment, but
+on LAN it's usually much easier to fire an attack, due to the speed of local
+network, where a bad guy can generate alot of packets in a short time.
+
+Spoofing of replies (Kaminsky attack, CVE-2008-1447)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+While udns uses random numbers for query IDs, it uses single UDP port for
+all queries (see previous item).  And even if it used random UDP port for
+each query, the attack described in CVE-2008-1447 is still quite trivial.
+This is not specific to udns library unfortunately - it is inherent property
+of the protocol.  Udns is designed to work in a LAN, it needs full recursive
+resolver nearby, and modern LAN usually uses high-bandwidth equipment which
+makes the Kaminsky attack trivial.  The problem is that even with qID (16
+bits) and random UDP port (about 20 bits available to a regular process)
+combined still can not hold enough randomness, so on a fast network it is
+still easy to flood the target with fake replies and hit the "right" reply
+before real reply comes.  So random qIDs don't add much protection anyway,
+even if this feature is implemented in udns, and using all available
+techniques wont solve it either.
+
+See also long comment in udns_resolver.c, udns_newid().
+
+Assumptions about RRs returned
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Currently udns processes records in the reply it received sequentially.
+This means that order of the records is significant.  For example, if
+we asked for foo.bar A, but the server returned that foo.bar is a CNAME
+(alias) for bar.baz, and bar.baz, in turn, has address 1.2.3.4, when
+the CNAME should come first in reply, followed by A.  While DNS specs
+does not say anything about order of records - it's an rrSET - unordered, -
+I think an implementation which returns the records in "wrong" order is
+somewhat insane...
+
+CNAME recursion
+~~~~~~~~~~~~~~~
+
+Another interesting point is the handling of CNAMEs returned as replies
+to non-CNAME queries.  If we asked for foo.bar A, but it's a CNAME, udns
+expects BOTH the CNAME itself and the target DN to be present in the reply.
+In other words, udns DOES NOT RECURSE CNAMES.  If we asked for foo.bar A,
+but only record in reply was that foo.bar is a CNAME for bar.baz, udns will
+return no records to an application (NXDOMAIN).  Strictly speaking, udns
+should repeat the query asking for bar.baz A, and recurse.  But since it's
+stub resolver, recursive resolver should recurse for us instead.
+
+It's not very difficult to implement, however.  Probably with some (global?)
+flag to en/dis-able the feature.  Provided there's some demand for it.
+
+To clarify: udns handles CNAME recursion in a single reply packet just fine.
+
+Note also that standard gethostbyname() routine does not recurse in this
+situation, too.
+
+Error reporting
+~~~~~~~~~~~~~~~
+
+Too many places in the code (various failure paths) sets generic "TEMPFAIL"
+error condition.  For example, if no nameserver replied to our query, an
+application will get generic TEMPFAIL, instead of something like TIMEDOUT.
+This probably should be fixed, but most applications don't care about the
+exact reasons of failure - 4 common cases are already too much:
+  - query returned some valid data
+  - NXDOMAIN
+  - valid domain but no data of requested type - =NXDOMAIN in most cases
+  - temporary error - this one sometimes (incorrectly!) treated as NXDOMAIN
+    by (naive) applications.
+DNS isn't yes/no, it's at least 3 variants, temp err being the 3rd important
+case!  And adding more variations for the temp error case is complicating things
+even more - again, from an application writer standpoint.  For diagnostics,
+such more specific error cases are of good help.
+
+Planned API changes
+~~~~~~~~~~~~~~~~~~~
+
+At least one thing I want to change for some future version is a way how
+queries are submitted and how replies are handled.
+
+I want to made dns_query object to be owned by an application.  So that instead
+of udns library allocating it for the lifetime of query, it will be pre-
+allocated by an application.  This simplifies and enhances query submitting
+interface, and complicates it a bit too, in simplest cases.
+
+Currently, we have:
+
+dns_submit_dn(dn, cls, typ, flags, parse, cbck, data)
+dns_submit_p(name, cls, typ, flags, parse, cbck, data)
+dns_submit_a4(ctx, name, flags, cbck, data)
+
+and so on -- with many parameters missed for type-specific cases, but generic
+cases being too complex for most common usage.
+
+Instead, with dns_query being owned by an app, we will be able to separately
+set up various parts of the query - domain name (various forms), type&class,
+parser, flags, callback...  and even change them at runtime.  And we will also
+be able to reuse query structures, instead of allocating/freeing them every
+time.  So the whole thing will look something like:
+
+ q = dns_alloc_query();
+ dns_submit(dns_q_flags(dns_q_a4(q, name, cbck), DNS_F_NOSRCH), data);
+
+The idea is to have a set of functions accepting struct dns_query* and
+returning it (so the calls can be "nested" like the above), to set up
+relevant parts of the query - specific type of callback, conversion from
+(type-specific) query parameters into a domain name (this is for type-
+specific query initializers), and setting various flags and options and
+type&class things.
+
+One example where this is almost essential - if we want to support
+per-query set of nameservers (which isn't at all useless: imagine a
+high-volume mail server, were we want to direct DNSBL queries to a separate
+set of nameservers, and rDNS queries to their own set and so on).  Adding
+another argument (set of nameservers to use) to EVERY query submitting
+routine is.. insane.  Especially since in 99% cases it will be set to
+default NULL.  But with such "nesting" of query initializers, it becomes
+trivial.
+
+This change (the way how queries gets submitted) will NOT break API/ABI
+compatibility with old versions, since the new submitting API works in
+parallel with current (and current will use the new one as building
+blocks, instead of doing all work at once).
+
+Another way to do the same is to manipulate query object right after a
+query has been submitted, but before any events processing (during this
+time, query object is allocated and initialized, but no actual network
+packets were sent - it will happen on the next event processing).  But
+this way it become impossible to perform syncronous resolver calls, since
+those calls hide query objects they use internally.
+
+Speaking of replies handling - the planned change is to stop using dynamic
+memory (malloc) inside the library.  That is, instead of allocating a buffer
+for a reply dynamically in a parsing routine (or memdup'ing the raw reply
+packet if no parsing routine is specified), I want udns to return the packet
+buffer it uses internally, and change parsing routines to expect a buffer
+for result.  When parsing, a routine will return true amount of memory it
+will need to place the result, regardless of whenever it has enough room
+or not, so that an application can (re)allocate properly sized buffer and
+call a parsing routine again.
+
+This, in theory, also can be done without breaking current API/ABI, but in
+that case we'll again need a parallel set of routines (parsing included),
+which makes the library more complicated with too many ways of doing the
+same thing.  Still, code reuse is at good level.
+
+Another modification I plan to include is to have an ability to work in
+terms of domain names (DNs) as used with on-wire DNS packets, not only
+with asciiz representations of them.  For this to work, the above two
+changes (query submission and result passing) have to be completed first
+(esp. the query submission part), so that it will be possible to specify
+some additional query flags (for example) to request domain names instead
+of the text strings, and to allow easy query submissions with either DNs
+or text strings.
diff --git a/3rdparty/udns/TODO b/3rdparty/udns/TODO
new file mode 100644 (file)
index 0000000..0dc9b96
--- /dev/null
@@ -0,0 +1,59 @@
+TODO
+
+The following is mostly an internal, not user-visible stuff.
+
+* rearrange an API to make dns_query object owned by application,
+  so that it'll look like this:
+   struct dns_query *q;
+   q = dns_query_alloc(ctx);
+   dns_query_set(q, options, domain_name, flags, ...);
+   dns_query_submit(ctx, q);
+  For more information see NOTES file, section "Planned API changes".
+
+* allow NULL callbacks?  Or provide separate resolver
+  context list of queries which are done but wich did not
+  have callback, and dns_pick() routine to retrieve results
+  from this query, i.e. allow non-callback usage?  The
+  non-callback usage may be handy sometimes (any *good*
+  example?), but it will be difficult to provide type-safe
+  non-callback interface due to various RR-specific types
+  in use.
+
+* DNS_OPT_FLAGS should be DNS_OPT_ADDFLAGS and DNS_OPT_SETFLAGS.
+  Currently one can't add a single flag bit but preserve
+  existing bits... at least not without retrieving all current
+  flags before, which isn't that bad anyway.
+
+* dns_set_opts() may process flags too (such as aaonly etc)
+
+* a way to disable $NSCACHEIP et al processing?
+  (with now separate dns_init() and dns_reset(), it has finer
+  control, but still no way to init from system files but ignore
+  environment variables and the like)
+
+* initialize/open the context automatically, and be more
+  liberal about initialization in general?
+
+* dns_init(ctx, do_open) - make the parameter opposite, aka
+  dns_init(ctx, skip_open) ?
+
+* allow TCP queue?
+
+* more accurate error reporting.  Currently, udns always returns TEMPFAIL,
+  but don't specify why it happened (ENOMEM, timeout, etc).
+
+* check the error value returned by recvfrom() and
+  sendto() and determine which errors to ignore.
+
+* maybe merge dns_timeouts() and dns_ioevent(), to have
+  only one entry point for everything?  For traditional
+  select-loop-based eventloop it may be easier, but for
+  callback-driven event loops the two should be separate.
+  Provide an option, or a single dns_events() entry point
+  for select-loop approach, or just call dns_ioevent()
+  from within dns_timeouts() (probably after renaming
+  it to be dns_events()) ?
+
+* implement /etc/hosts lookup too, ala [c-]ares??
+
+* sortlist support?
diff --git a/3rdparty/udns/config.h.in b/3rdparty/udns/config.h.in
new file mode 100644 (file)
index 0000000..d306828
--- /dev/null
@@ -0,0 +1,4 @@
+#cmakedefine HAVE_POLL
+#cmakedefine HAVE_GETOPT
+#cmakedefine HAVE_INET_PTON_NTOP
+#cmakedefine HAVE_IPv6
diff --git a/3rdparty/udns/configure b/3rdparty/udns/configure
new file mode 100755 (executable)
index 0000000..dda98b3
--- /dev/null
@@ -0,0 +1,165 @@
+#! /bin/sh
+# autoconf-style configuration script
+#
+
+set -e
+
+name=udns
+
+if [ -f udns.h -a -f udns_resolver.c ] ; then :
+else
+  echo "configure: error: sources not found at `pwd`" >&2
+  exit 1
+fi
+
+options="ipv6"
+
+for opt in $options; do
+  eval enable_$opt=
+done
+
+if [ -f config.status ]; then
+  . ./config.status
+fi
+
+enable() {
+  opt=`echo "$1" | sed 's/^--[^-]*-//'`
+  case "$opt" in
+    ipv6) ;;
+    *) echo "configure: unrecognized option \`$1'" >&2; exit 1;;
+  esac
+  eval enable_$opt=$2
+}
+
+while [ $# -gt 0 ]; do
+  case "$1" in
+    --disable-*|--without-*|--no-*) enable "$1" n;;
+    --enable-*|--with-*) enable "$1" y;;
+    --help | --hel | --he | --h | -help | -hel | -he | -h )
+      cat <<EOF
+configure: configure $name package.
+Usage: ./configure [options]
+where options are:
+ --enable-option, --with-option --
+   enable the named option/feature
+ --disable-option, --without-option, --no-option --
+   disable the named option/feature
+ --help - print this help and exit
+Optional features (all enabled by default if system supports a feature):
+  ipv6 - enable/disable IP version 6 (IPv6) support
+EOF
+      exit 0
+      ;;
+    *) echo "configure: unknown option \`$1'" >&2; exit 1 ;;
+  esac
+  shift
+done
+
+. ./configure.lib
+
+ac_msg "configure"
+ac_result "$name package"
+
+ac_prog_c_compiler_v
+ac_prog_ranlib_v
+
+ac_ign ac_yesno "for getopt()" ac_have GETOPT ac_link <<EOF
+#include <stdio.h>
+extern int optind;
+extern char *optarg;
+extern int getopt(int, char **, char *);
+int main(int argc, char **argv) {
+  getopt(argc, argv, "abc");
+  return optarg ? optind : 0;
+}
+EOF
+
+if ac_library_find_v 'socket and connect' "" "-lsocket -lnsl" <<EOF
+int main() { socket(); connect(); return 0; }
+EOF
+then :
+else
+  ac_fatal "cannot find libraries needed for sockets"
+fi
+
+ac_ign \
+ ac_yesno "for inet_pton() && inet_ntop()" \
+ ac_have INET_PTON_NTOP \
+ ac_link <<EOF
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+int main() {
+  char buf[64];
+  long x = 0;
+  inet_pton(AF_INET, &x, buf);
+  return inet_ntop(AF_INET, &x, buf, sizeof(buf));
+} 
+EOF
+
+if ac_yesno "for socklen_t" ac_compile <<EOF
+#include <sys/types.h>
+#include <sys/socket.h>
+int foo() { socklen_t len; len = 0; return len; }
+EOF
+then :
+else
+  ac_define socklen_t int
+fi
+
+if [ n != "$enable_ipv6" ]; then
+if ac_yesno "for IPv6" ac_have IPv6 ac_compile <<EOF
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+int main() {
+  struct sockaddr_in6 sa;
+  sa.sin6_family = AF_INET6;
+  return 0;
+}
+EOF
+then :
+elif [ "$enable_ipv6" ]; then
+  ac_fatal "IPv6 is requested but not available"
+fi
+fi # !disable_ipv6?
+
+if ac_yesno "for poll()" ac_have POLL ac_link <<EOF
+#include <sys/types.h>
+#include <sys/poll.h>
+int main() {
+  struct pollfd pfd[2];
+  return poll(pfd, 2, 10);
+}
+EOF
+then :
+else
+  ac_ign ac_yesno "for sys/select.h" ac_have SYS_SELECT_H ac_cpp <<EOF
+#include <sys/types.h>
+#include <sys/select.h>
+EOF
+fi
+
+ac_config_h
+ac_output Makefile
+ac_msg "creating config.status"
+rm -f config.status
+{
+echo "# automatically generated by configure to hold command-line options"
+echo
+found=
+for opt in $options; do
+  eval val=\$enable_$opt
+  if [ -n "$val" ]; then
+    echo enable_$opt=$val
+    found=y
+  fi
+done
+if [ ! "$found" ]; then
+  echo "# (no options encountered)"
+fi
+} > config.status
+ac_result ok
+
+ac_result "all done."
+exit 0
diff --git a/3rdparty/udns/configure.lib b/3rdparty/udns/configure.lib
new file mode 100644 (file)
index 0000000..541177a
--- /dev/null
@@ -0,0 +1,268 @@
+# configure.lib
+# a library of shell routines for simple autoconf system
+#
+
+set -e
+ac_substitutes=
+rm -f conftest* config.log
+exec 5>config.log
+cat <<EOF >&5
+This file contains any messages produced by compilers etc while
+running configure, to aid debugging if configure script makes a mistake.
+
+EOF
+
+case `echo "a\c"` in
+  *c*) ac_en=-n ac_ec= ;;
+  *)   ac_en= ac_ec='\c' ;;
+esac
+
+##### Messages
+ac_msg() {
+  echo $ac_en "$*... $ac_ec"
+  echo ">>> $*" >&5
+}
+ac_checking() {
+  echo $ac_en "checking $*... $ac_ec"
+  echo ">>> checking $*" >&5
+}
+ac_result() {
+  echo "$1"
+  echo "=== $1" >&5
+}
+ac_fatal() {
+  echo "configure: fatal: $*" >&2
+  echo "=== FATAL: $*" >&5
+  exit 1
+}
+ac_warning() {
+  echo "configure: warning: $*" >&2
+  echo "=== WARNING: $*" >&5
+}
+ac_ign() {
+  "$@" || :
+}
+
+# ac_run command...
+# captures output in conftest.out
+ac_run() {
+  # apparently UnixWare (for one) /bin/sh optimizes the following "if"
+  # "away", by checking if there's such a command BEFORE redirecting
+  # output.  So error message (like "gcc: command not found") goes
+  # to stderr instead of to conftest.out, and `cat conftest.out' below
+  # fails.
+  if "$@" >conftest.out 2>&1; then
+    return 0
+  else
+    echo "==== Command invocation failed. Command line was:" >&5
+    echo "$*" >&5
+    echo "==== compiler input was:" >&5
+    cat conftest.c >&5
+    echo "==== output was:" >&5
+    cat conftest.out >&5
+    echo "====" >&5
+    return 1
+  fi
+}
+
+# common case for ac_verbose: yes/no result
+ac_yesno() {
+  ac_checking "$1"
+  shift
+  if "$@"; then
+    ac_result yes
+    return 0
+  else
+    ac_result no
+    return 1
+  fi
+}
+
+ac_subst() {
+  ac_substitutes="$ac_substitutes $*"
+}
+
+ac_define() {
+  CDEFS="$CDEFS -D$1=${2:-1}"
+}
+
+ac_have() {
+  ac_what=$1; shift
+  if "$@"; then
+    ac_define HAVE_$ac_what
+    eval ac_have_$ac_what=yes
+    return 0
+  else
+    eval ac_have_$ac_what=no
+    return 1
+  fi
+}
+
+##### Compiling, linking
+
+# run a compiler
+ac_run_compiler() {
+  rm -f conftest*; cat >conftest.c
+  ac_run $CC $CFLAGS $CDEFS "$@" conftest.c
+}
+
+ac_compile() {
+  ac_run_compiler -c
+}
+
+ac_link() {
+  ac_run_compiler -o conftest $LIBS "$@"
+}
+
+ac_cpp() {
+  ac_run_compiler -E "$@"
+}
+
+### check for C compiler.  Set $CC, $CFLAGS etc
+ac_prog_c_compiler_v() {
+  ac_checking "for C compiler"
+  rm -f conftest*
+  echo 'int main(int argc, char **argv) { return 0; }' >conftest.c
+
+  if [ -n "$CC" ]; then
+    if ac_run $CC -o conftest conftest.c && ac_run ./conftest; then
+      ac_result "\$CC ($CC)"
+    else
+      ac_result no
+      ac_fatal "\$CC ($CC) is not a working compiler"
+    fi
+  else
+    for cc in gcc cc ; do
+      if ac_run $cc -o conftest conftest.c && ac_run ./conftest; then
+        ac_result "$cc"
+        CC=$cc
+        break
+      fi
+    done
+    if [ -z "$CC" ]; then
+      ac_result no
+      ac_fatal "no working C compiler found in \$PATH. please set \$CC variable"
+    fi
+  fi
+  if [ -z "$CFLAGS" ]; then
+    if ac_yesno "whenever C compiler ($CC) is GNU CC" \
+        ac_grep_cpp yEs_mAsTeR <<EOF
+#ifdef __GNUC__
+  yEs_mAsTeR;
+#endif
+EOF
+    then
+      CFLAGS="-Wall -W -O2 -pipe"
+    else
+      CFLAGS=-O
+    fi
+  fi
+  cc="$CC $CFLAGS"
+  ccld="$cc"
+  if [ -n "$LDFLAGS" ]; then ccld="$ccld $LDFLAGS"; fi
+  if [ -n "$LIBS" ]; then ccld="$ccld $LIBS"; fi
+  if ac_yesno "whenever the C compiler ($ccld)
+           can produce executables" \
+     ac_compile_run <<EOF
+int main() { return 0; }
+EOF
+  then :
+  else
+    ac_fatal "no working C compiler found"
+  fi
+  LD='$(CC)'
+  [ -n "$AR" ] || AR=ar
+  [ -n "$ARFLAGS" ] || ARFLAGS=rv
+  [ -n "$AWK" ] || AWK=awk
+  ac_substitutes="$ac_substitutes CC CFLAGS CDEFS LD LDFLAGS LIBS AR ARFLAGS AWK"
+}
+
+
+ac_prog_ranlib_v() {
+  ac_checking "for ranlib"
+  if [ -n "$RANLIB" ]; then
+    ac_result "\$RANLIB ($RANLIB)"
+  else
+    ifs="$IFS"
+    IFS=:
+    for dir in $PATH; do
+      [ -n "$dir" ] || dir=.
+      if [ -f $dir/ranlib ]; then
+        RANLIB=ranlib
+        break
+      fi
+    done
+    IFS="$ifs"
+    if [ -z "$RANLIB" ]; then ac_result no; RANLIB=:
+    else ac_result "$RANLIB"
+    fi
+  fi
+  ac_substitutes="$ac_substitutes RANLIB"
+}
+
+ac_library_find_v() {
+  ac_checking "for libraries needed for $1"
+  shift
+  fond=
+  rm -f conftest*; cat >conftest.c
+  for lib in "$@"; do
+    if ac_run $CC $CFLAGS $LDFLAGS conftest.c -o conftest $LIBS $lib; then
+      found=y
+      break
+    fi
+  done
+  if [ ! "$found" ]; then
+    ac_result "not found"
+    return 1
+  fi
+  if [ -z "$lib" ]; then
+    ac_result "ok (none needed)"
+  else
+    ac_result "ok ($lib)"
+    LIBS="$LIBS $lib"
+  fi
+}
+
+ac_compile_run() {
+  ac_link "$@" && ac_run ./conftest
+}
+
+ac_grep_cpp() {
+  pattern="$1"; shift
+  ac_cpp "$@" && grep "$pattern" conftest.out >/dev/null
+}
+
+ac_output() {
+  for var in $ac_substitutes; do
+    eval echo "\"s|@$var@|\$$var|\""
+  done >conftest.sed
+  for file in "$@"; do
+    ac_msg "creating $file"
+    if [ -f $file.in ]; then
+      sed -f conftest.sed $file.in > $file.tmp
+      mv -f $file.tmp $file
+      ac_result ok
+    else
+      ac_result failed
+      ac_fatal "$file.in not found"
+    fi
+  done
+  rm -f conftest*
+}
+
+ac_config_h() {
+  h=${1:-config.h}
+  ac_msg "creating $h"
+  rm -f $1.tmp
+  echo "/* automatically generated by configure. */" > $h.tmp
+  echo "$CDEFS" | tr ' ' '
+' | sed -e 's/^-D/#define /' -e 's/=/ /' >> $h.tmp
+  if [ -f $h ] && cmp -s $h.tmp $h ; then
+    rm -f $h.tmp
+    ac_result unchanged
+  else
+    mv -f $h.tmp $h
+    ac_result ok
+  fi
+  CDEFS=-DHAVE_CONFIG_H
+}
diff --git a/3rdparty/udns/dnsget.1 b/3rdparty/udns/dnsget.1
new file mode 100644 (file)
index 0000000..200557f
--- /dev/null
@@ -0,0 +1,195 @@
+.\" dnsget.1: dnsget manpage
+.\"
+.\" Copyright (C) 2005-2014  Michael Tokarev <mjt+udns@tls.msk.ru>
+.\" This file is part of UDNS library, an async DNS stub resolver.
+.\"
+.\" This library is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU Lesser General Public
+.\" License as published by the Free Software Foundation; either
+.\" version 2.1 of the License, or (at your option) any later version.
+.\"
+.\" This library 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
+.\" Lesser General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU Lesser General Public
+.\" License along with this library, in file named COPYING.LGPL; if not,
+.\" write to the Free Software Foundation, Inc., 59 Temple Place,
+.\" Suite 330, Boston, MA  02111-1307  USA
+
+.TH dnsget 1 "Jan 2014" "User Utilities"
+
+.SH NAME
+dnsget \- DNS lookup utility
+
+.SH SYNOPSYS
+.B dnsget
+.RB [\| \-v \||\| \-q \|]
+.RB [\| \-c
+.IR class \|]
+.RB [\| \-t
+.IR type \|]
+.RB [\| \-o
+.IR opt , opt ,...]
+.IR name \|.\|.\|.
+
+.SH DESCRIPTION
+.B dnsget
+is a simple command-line to perform DNS lookups, similar to
+.BR host (1)
+and
+.BR dig (1).
+It is useable for both interactive/debugging scenarious and
+in scripts.
+The program is implemented using
+.BR udns (3)
+library.
+
+.PP
+By default,
+.B dnsget
+produces a human-readable output, similar to
+.RS
+.nf
+alias.example.com. CNAME www.example.com.
+www.example.com. A 192.168.1.1
+www.example.com. MX 10 mx.example.com.
+.fi
+.RE
+which is just sufficient to see how a given name resolves.
+Output format is controllable with
+.B \-v
+and
+.B \-q
+options -- the former increases verbosity level up to printing
+the whole DNS contents of all packets sent and received, which
+is suitable for debugging DNS problems, while the latter reduces
+the level, making output more quiet, up to bare result with no
+error messages, which is good for scripts.
+
+.SH OPTIONS
+
+The following options are recognized by
+.BR dnsget :
+
+.TP
+.B \-v
+produce more detailed output.  More
+.BR \-v 's
+means more details will be produced.  With single
+.BR \-v , dnsget
+will print contents of all received DNS packets (in a readable format),
+while with
+.BR \-vv ,
+it will output all outgoing DNS packets too.
+
+.TP
+.B \-q
+the opposite for \fB\-v\fR -- produce less detailed output.
+With single
+.BR \-q , dnsget
+will only show (decoded) data from final DNS resource records (RR),
+while
+.B \-qq
+also suppresses error messages.
+
+.TP
+\fB\-t \fItype\fR
+request record(s) of the given type \fItype\fR.  By default,
+.B dnsget
+will ask for IPv4 address (A) record, or for PTR record if the
+argument in question is an IPv4 or IPv6 address.  Recognized
+types include A, AAAA, MX, TXT, CNAME, PTR, NS, SOA, ANY and
+others.
+
+.TP
+\fB\-c \fIclass\fR
+request DNS record(s) of the given class \fIclass\fR.  By
+default
+.B dnsget
+uses IN class.  Valid classes include IN, CH, HS, ANY.
+
+.TP
+.B \-a
+(compatibility option).  Equivalent to setting query type to
+.B ANY
+and increasing verbosity level
+.RB ( \-v ).
+
+.TP
+.B \-C
+(planned)
+
+.TP
+.B \-x
+(planned)
+
+.TP
+\fB\-o \fIopt\fR,\fIopt\fR,...
+(may be specified several times).
+Set resolver options (in a form \fIoption\fR:\fIvalue\fR) as if they
+were set in
+.RB $ RES_OPTIONS
+environment variable, or set query flags:
+.RS
+.TP
+\fBtimeout\fR:\fIsec\fR
+Set initial query timeout to \fIsec\fR.
+.TP
+\fBattempts\fR:\fInum\fR
+(re)try every query \fInum\fR times before failing.
+.TP
+\fBudpbuf\fR:\fIbytes\fR
+set DNS UDP buffer size to \fIbytes\fR bytes.  Valid values
+are from 512 to 65535.  If \fIbytes\fR is greather than 512,
+EDNS0 (RFC 2671) extensions will be used.
+.TP
+\fBport\fR:\fInum\fR
+Use given UDP port number \fInum\fR instead of the default port 53 (domain).
+.TP
+\fBaa\fR
+set AA (auth only) query bit.
+.TP
+\fBnord\fR
+do not set RD (recursion desired) query bit (set by default).
+.TP
+\fBdnssec\fR or \fBdo\fR
+set DNSSEC OK (DO) query flag (\fBdnsget\fR does not verify DNSSEC signatures,
+only displays them; this is set in EDNS RR).
+.TP
+\fBcd\fR
+set CD (checking disabled) query bit.
+.RE
+
+.TP
+\fB\-n \fInameserver\fR
+Use the given nameserver(s) (may be specified more than once)
+instead of the default.  Using this option has the same same effect as 
+.RB $ NSCACHEIP
+or
+.RB $ NAMESERVERS
+environment variables, with the only difference that only IPv4 addresses
+are recognized for now, and it is possible to specify names (which will
+be resolved using default settings) instead of IP addresses.
+
+.TP
+.B \-h
+print short help and exit.
+
+.SH "RETURN VALUE"
+When all names where resovled successefully,
+.B dnsget
+exits with zero exit status.  If at least one name was not found,
+.B dnsget
+will exit with return code 100.  If some other error occured during
+name resolution, it will exit with code 99.  In case of usage or
+initialization error,
+.B dnsget
+will return 1.
+
+.SH "SEE ALSO"
+.BR host (1)
+.BR dig (1)
+.BR resolv.conf (5)
+.BR udns (3).
diff --git a/3rdparty/udns/dnsget.c b/3rdparty/udns/dnsget.c
new file mode 100644 (file)
index 0000000..417e8d9
--- /dev/null
@@ -0,0 +1,759 @@
+/* dnsget.c
+   simple host/dig-like application using UDNS library
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#ifdef WINDOWS
+#include <windows.h>
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "udns.h"
+
+#ifndef HAVE_GETOPT
+# include "getopt.c"
+#endif
+
+#ifndef AF_INET6
+# define AF_INET6 10
+#endif
+
+static char *progname;
+static int verbose = 1;
+static int errors;
+static int notfound;
+
+/* verbosity level:
+ * <0 - bare result
+ *  0 - bare result and error messages
+ *  1 - readable result
+ *  2 - received packet contents and `trying ...' stuff
+ *  3 - sent and received packet contents
+ */
+
+static void die(int errnum, const char *fmt, ...) {
+  va_list ap;
+  fprintf(stderr, "%s: ", progname);
+  va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
+  if (errnum) fprintf(stderr, ": %s\n", strerror(errnum));
+  else putc('\n', stderr);
+  fflush(stderr);
+  exit(1);
+}
+
+static const char *dns_xntop(int af, const void *src) {
+  static char buf[6*5+4*4];
+  return dns_ntop(af, src, buf, sizeof(buf));
+}
+
+struct query {
+  const char *name;            /* original query string */
+  unsigned char *dn;           /* the DN being looked up */
+  enum dns_type qtyp;          /* type of the query */
+};
+
+static void query_free(struct query *q) {
+  free(q->dn);
+  free(q);
+}
+
+static struct query *
+query_new(const char *name, const unsigned char *dn, enum dns_type qtyp) {
+  struct query *q = malloc(sizeof(*q));
+  unsigned l = dns_dnlen(dn);
+  unsigned char *cdn = malloc(l);
+  if (!q || !cdn) die(0, "out of memory");
+  memcpy(cdn, dn, l);
+  q->name = name;
+  q->dn = cdn;
+  q->qtyp = qtyp;
+  return q;
+}
+
+static enum dns_class qcls = DNS_C_IN;
+
+static void
+dnserror(struct query *q, int errnum) {
+  if (verbose >= 0)
+    fprintf(stderr, "%s: unable to lookup %s record for %s: %s\n", progname,
+            dns_typename(q->qtyp), dns_dntosp(q->dn), dns_strerror(errnum));
+  if (errnum == DNS_E_NXDOMAIN || errnum == DNS_E_NODATA)
+    ++notfound;
+  else
+    ++errors;
+  query_free(q);
+}
+
+static const unsigned char *
+printtxt(const unsigned char *c) {
+  unsigned n = *c++;
+  const unsigned char *e = c + n;
+  if (verbose > 0) while(c < e) {
+    if (*c < ' ' || *c >= 127) printf("\\%03u", *c);
+    else if (*c == '\\' || *c == '"') printf("\\%c", *c);
+    else putchar(*c);
+    ++c;
+  }
+  else
+   fwrite(c, n, 1, stdout);
+  return e;
+}
+
+static void
+printhex(const unsigned char *c, const unsigned char *e) {
+  while(c < e)
+    printf("%02x", *c++);
+}
+
+static unsigned char to_b64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void
+printb64(const unsigned char *c, const unsigned char *e) {
+  while(c < e) {
+    putchar(to_b64[c[0] >> 2]);
+    if (c+1 < e) {
+      putchar(to_b64[(c[0] & 0x3) << 4 | c[1] >> 4]);
+      if (c+2 < e) {
+        putchar(to_b64[(c[1] & 0xf) << 2 | c[2] >> 6]);
+        putchar(to_b64[c[2] & 0x3f]);
+      }
+      else {
+        putchar(to_b64[(c[1] & 0xf) << 2]);
+       putchar('=');
+       break;
+      }
+    }
+    else {
+      putchar(to_b64[(c[0] & 0x3) << 4]);
+      putchar('=');
+      putchar('=');
+      break;
+    }
+    c += 3;
+  }
+}
+
+static void
+printdate(time_t time) {
+  struct tm *tm = gmtime(&time);
+  printf("%04d%02d%02d%02d%02d%02d",
+    tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+    tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+static void
+printrr(const struct dns_parse *p, struct dns_rr *rr) {
+  const unsigned char *pkt = p->dnsp_pkt;
+  const unsigned char *end = p->dnsp_end;
+  const unsigned char *dptr = rr->dnsrr_dptr;
+  const unsigned char *dend = rr->dnsrr_dend;
+  unsigned char *dn = rr->dnsrr_dn;
+  const unsigned char *c;
+  unsigned n;
+
+  if (verbose > 0) {
+    if (verbose > 1) {
+      if (!p->dnsp_rrl && !rr->dnsrr_dn[0] && rr->dnsrr_typ == DNS_T_OPT) {
+        printf(";EDNS%d OPT record (UDPsize: %d, ERcode: %d, Flags: 0x%02x): %d bytes\n",
+               (rr->dnsrr_ttl>>16) & 0xff,     /* version */
+               rr->dnsrr_cls,                  /* udp size */
+               (rr->dnsrr_ttl>>24) & 0xff,     /* extended rcode */
+               rr->dnsrr_ttl & 0xffff,         /* flags */
+               rr->dnsrr_dsz);
+        return;
+      }
+      n = printf("%s.", dns_dntosp(rr->dnsrr_dn));
+      printf("%s%u\t%s\t%s\t",
+             n > 15 ? "\t" : n > 7 ? "\t\t" : "\t\t\t",
+             rr->dnsrr_ttl,
+             dns_classname(rr->dnsrr_cls),
+             dns_typename(rr->dnsrr_typ));
+    }
+    else
+      printf("%s. %s ", dns_dntosp(rr->dnsrr_dn), dns_typename(rr->dnsrr_typ));
+  }
+
+  switch(rr->dnsrr_typ) {
+
+  case DNS_T_CNAME:
+  case DNS_T_PTR:
+  case DNS_T_NS:
+  case DNS_T_MB:
+  case DNS_T_MD:
+  case DNS_T_MF:
+  case DNS_T_MG:
+  case DNS_T_MR:
+    if (dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN) <= 0) goto xperr;
+    printf("%s.", dns_dntosp(dn));
+    break;
+
+  case DNS_T_A:
+    if (rr->dnsrr_dsz != 4) goto xperr;
+    printf("%d.%d.%d.%d", dptr[0], dptr[1], dptr[2], dptr[3]);
+    break;
+
+  case DNS_T_AAAA:
+    if (rr->dnsrr_dsz != 16) goto xperr;
+    printf("%s", dns_xntop(AF_INET6, dptr));
+    break;
+
+  case DNS_T_MX:
+    c = dptr + 2;
+    if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
+    printf("%d %s.", dns_get16(dptr), dns_dntosp(dn));
+    break;
+
+  case DNS_T_TXT:
+    /* first verify it */
+    for(c = dptr; c < dend; c += n) {
+      n = *c++;
+      if (c + n > dend) goto xperr;
+    }
+    c = dptr; n = 0;
+    while (c < dend) {
+      if (verbose > 0) printf(n++ ? "\" \"":"\"");
+      c = printtxt(c);
+    }
+    if (verbose > 0) putchar('"');
+    break;
+
+  case DNS_T_HINFO:    /* CPU, OS */
+    c = dptr;
+    n = *c++; if ((c += n) >= dend) goto xperr;
+    n = *c++; if ((c += n) != dend) goto xperr;
+    c = dptr;
+    if (verbose > 0) putchar('"');
+    c = printtxt(c);
+    if (verbose > 0) printf("\" \""); else putchar(' ');
+    printtxt(c);
+    if (verbose > 0) putchar('"');
+    break;
+
+  case DNS_T_WKS:
+    c = dptr;
+    if (dptr + 4 + 2 >= end) goto xperr;
+    printf("%s %d", dns_xntop(AF_INET, dptr), dptr[4]);
+    c = dptr + 5;
+    for (n = 0; c < dend; ++c, n += 8) {
+      if (*c) {
+        unsigned b;
+        for (b = 0; b < 8; ++b)
+          if (*c & (1 << (7-b))) printf(" %d", n + b);
+      }
+    }
+    break;
+
+  case DNS_T_SRV:      /* prio weight port targetDN */
+    c = dptr;
+    c += 2 + 2 + 2;
+    if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
+    c = dptr;
+    printf("%d %d %d %s.",
+           dns_get16(c+0), dns_get16(c+2), dns_get16(c+4),
+           dns_dntosp(dn));
+    break;
+
+  case DNS_T_NAPTR:    /* order pref flags serv regexp repl */
+    c = dptr;
+    c += 4;    /* order, pref */
+    for (n = 0; n < 3; ++n)
+      if (c >= dend) goto xperr;
+      else c += *c + 1;
+    if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 || c != dend) goto xperr;
+    c = dptr;
+    printf("%u %u", dns_get16(c+0), dns_get16(c+2));
+    c += 4;
+    for(n = 0; n < 3; ++n) {
+      putchar(' ');
+      if (verbose > 0) putchar('"');
+      c = printtxt(c);
+      if (verbose > 0) putchar('"');
+    } 
+    printf(" %s.", dns_dntosp(dn));
+    break;
+
+  case DNS_T_KEY:
+  case DNS_T_DNSKEY:
+    /* flags(2) proto(1) algo(1) pubkey */
+  case DNS_T_DS:
+  case DNS_T_DLV:
+    /* ktag(2) proto(1) algo(1) pubkey */
+    c = dptr;
+    if (c + 2 + 1 + 1 > dend) goto xperr;
+    printf("%d %d %d", dns_get16(c), c[2], c[3]);
+    c += 2 + 1 + 1;
+    if (c < dend) {
+      putchar(' ');
+      printb64(c, dend);
+    }
+    break;
+
+  case DNS_T_SIG:
+  case DNS_T_RRSIG:
+    /* type(2) algo(1) labels(1) ottl(4) sexp(4) sinc(4) tag(2) sdn sig */
+    c = dptr;
+    c += 2 + 1 + 1 + 4 + 4 + 4 + 2;
+    if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr;
+    printf("%s %u %u %u ",
+           dns_typename(dns_get16(dptr)), dptr[2], dptr[3], dns_get32(dptr+4));
+    printdate(dns_get32(dptr+8));
+    putchar(' ');
+    printdate(dns_get32(dptr+12));
+    printf(" %d %s. ", dns_get16(dptr+10), dns_dntosp(dn));
+    printb64(c, dend);
+    break;
+
+  case DNS_T_SSHFP: /* algo(1), fp type(1), fp... */
+    if (dend < dptr + 3) goto xperr;
+    printf("%u %u ", dptr[0], dptr[1]); /* algo, fp type */
+    printhex(dptr + 2, dend);
+    break;
+
+#if 0  /* unused RR types? */
+  case DNS_T_NSEC: /* nextDN bitmaps */
+    c = dptr;
+    if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0) goto xperr;
+    printf("%s.", dns_dntosp(dn));
+    unfinished.
+    break;
+#endif
+
+
+  case DNS_T_SOA:
+    c = dptr;
+    if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
+        dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
+        c + 4*5 != dend)
+      goto xperr;
+    dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
+    printf("%s. ", dns_dntosp(dn));
+    dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
+    printf("%s. ", dns_dntosp(dn));
+    printf("%u %u %u %u %u",
+           dns_get32(dptr), dns_get32(dptr+4), dns_get32(dptr+8),
+           dns_get32(dptr+12), dns_get32(dptr+16));
+    break;
+
+  case DNS_T_MINFO:
+    c = dptr;
+    if (dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
+        dns_getdn(pkt, &c, end, dn, DNS_MAXDN) <= 0 ||
+       c != dend)
+      goto xperr;
+    dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
+    printf("%s. ", dns_dntosp(dn));
+    dns_getdn(pkt, &dptr, end, dn, DNS_MAXDN);
+    printf("%s.", dns_dntosp(dn));
+    break;
+
+  case DNS_T_NULL:
+  default:
+    printhex(dptr, dend);
+    break;
+  }
+  putchar('\n');
+  return;
+
+xperr:
+  printf("<parse error>\n");
+  ++errors;
+}
+
+static int
+printsection(struct dns_parse *p, int nrr, const char *sname) {
+  struct dns_rr rr;
+  int r;
+  if (!nrr) return 0;
+  if (verbose > 1) printf("\n;; %s section (%d):\n", sname, nrr);
+
+  p->dnsp_rrl = nrr;
+  while((r = dns_nextrr(p, &rr)) > 0)
+    printrr(p, &rr);
+  if (r < 0) printf("<<ERROR>>\n");
+  return r;
+}
+
+/* dbgcb will only be called if verbose > 1 */
+static void
+dbgcb(int code, const struct sockaddr *sa, unsigned slen,
+      const unsigned char *pkt, int r,
+      const struct dns_query *unused_q, void *unused_data) {
+  struct dns_parse p;
+  const unsigned char *cur, *end;
+  int numqd;
+
+  if (code > 0)        {
+    printf(";; trying %s.\n", dns_dntosp(dns_payload(pkt)));
+    printf(";; sending %d bytes query to ", r);
+  }
+  else
+    printf(";; received %d bytes response from ", r);
+  if (sa->sa_family == AF_INET && slen >= sizeof(struct sockaddr_in))
+    printf("%s port %d\n",
+           dns_xntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr),
+           htons(((struct sockaddr_in*)sa)->sin_port));
+#ifdef HAVE_IPv6
+  else if (sa->sa_family == AF_INET6 && slen >= sizeof(struct sockaddr_in6))
+    printf("%s port %d\n",
+           dns_xntop(AF_INET6, &((struct sockaddr_in6*)sa)->sin6_addr),
+           htons(((struct sockaddr_in6*)sa)->sin6_port));
+#endif
+  else
+    printf("<<unknown socket type %d>>\n", sa->sa_family);
+  if (code > 0 && verbose < 3) {
+    putchar('\n');
+    return;
+  }
+
+  if (code == -2) printf(";; reply from unexpected source\n");
+  if (code == -5) printf(";; reply to a query we didn't sent (or old)\n");
+  if (r < DNS_HSIZE) {
+    printf(";; short packet (%d bytes)\n", r);
+    return;
+  }
+  if (dns_opcode(pkt) != 0)
+    printf(";; unexpected opcode %d\n", dns_opcode(pkt));
+  if (dns_tc(pkt) != 0)
+    printf(";; warning: TC bit set, probably incomplete reply\n");
+
+  printf(";; ->>HEADER<<- opcode: ");
+  switch(dns_opcode(pkt)) {
+  case 0: printf("QUERY"); break;
+  case 1: printf("IQUERY"); break;
+  case 2: printf("STATUS"); break;
+  default: printf("UNKNOWN(%u)", dns_opcode(pkt)); break;
+  }
+  printf(", status: %s, id: %d, size: %d\n;; flags:",
+         dns_rcodename(dns_rcode(pkt)), dns_qid(pkt), r);
+  if (dns_qr(pkt)) printf(" qr");
+  if (dns_aa(pkt)) printf(" aa");
+  if (dns_tc(pkt)) printf(" tc");
+  if (dns_rd(pkt)) printf(" rd");
+  if (dns_ra(pkt)) printf(" ra");
+  /* if (dns_z(pkt))  printf(" z"); only one reserved bit left */
+  if (dns_ad(pkt)) printf(" ad");
+  if (dns_cd(pkt)) printf(" cd");
+  numqd = dns_numqd(pkt);
+  printf("; QUERY: %d, ANSWER: %d, AUTHORITY: %d, ADDITIONAL: %d\n",
+         numqd, dns_numan(pkt), dns_numns(pkt), dns_numar(pkt));
+  if (numqd != 1)
+    printf(";; unexpected number of entries in QUERY section: %d\n",
+           numqd);
+  printf("\n;; QUERY SECTION (%d):\n", numqd);
+  cur = dns_payload(pkt);
+  end = pkt + r;
+  while(numqd--) {
+    if (dns_getdn(pkt, &cur, end, p.dnsp_dnbuf, DNS_MAXDN) <= 0 ||
+        cur + 4 > end) {
+      printf("; invalid query section\n");
+      return;
+    }
+    r = printf(";%s.", dns_dntosp(p.dnsp_dnbuf));
+    printf("%s%s\t%s\n",
+           r > 23 ? "\t" : r > 15 ? "\t\t" : r > 7 ? "\t\t\t" : "\t\t\t\t",
+           dns_classname(dns_get16(cur+2)), dns_typename(dns_get16(cur)));
+    cur += 4;
+  }
+
+  p.dnsp_pkt = pkt;
+  p.dnsp_cur = p.dnsp_ans = cur;
+  p.dnsp_end = end;
+  p.dnsp_qdn = NULL;
+  p.dnsp_qcls = p.dnsp_qtyp = 0;
+  p.dnsp_ttl = 0xffffffffu;
+  p.dnsp_nrr = 0;
+
+  r = printsection(&p, dns_numan(pkt), "ANSWER");
+  if (r == 0)
+    r = printsection(&p, dns_numns(pkt), "AUTHORITY");
+  if (r == 0)
+    r = printsection(&p, dns_numar(pkt), "ADDITIONAL");
+  putchar('\n');
+}
+
+static void dnscb(struct dns_ctx *ctx, void *result, void *data) {
+  int r = dns_status(ctx);
+  struct query *q = data;
+  struct dns_parse p;
+  struct dns_rr rr;
+  unsigned nrr;
+  unsigned char dn[DNS_MAXDN];
+  const unsigned char *pkt, *cur, *end;
+  if (!result) {
+    dnserror(q, r);
+    return;
+  }
+  pkt = result; end = pkt + r; cur = dns_payload(pkt);
+  dns_getdn(pkt, &cur, end, dn, sizeof(dn));
+  dns_initparse(&p, NULL, pkt, cur, end);
+  p.dnsp_qcls = p.dnsp_qtyp = 0;
+  nrr = 0;
+  while((r = dns_nextrr(&p, &rr)) > 0) {
+    if (!dns_dnequal(dn, rr.dnsrr_dn)) continue;
+    if ((qcls == DNS_C_ANY || qcls == rr.dnsrr_cls) &&
+        (q->qtyp == DNS_T_ANY || q->qtyp == rr.dnsrr_typ))
+      ++nrr;
+    else if (rr.dnsrr_typ == DNS_T_CNAME && !nrr) {
+      if (dns_getdn(pkt, &rr.dnsrr_dptr, end,
+                    p.dnsp_dnbuf, sizeof(p.dnsp_dnbuf)) <= 0 ||
+          rr.dnsrr_dptr != rr.dnsrr_dend) {
+        r = DNS_E_PROTOCOL;
+        break;
+      }
+      else {
+        if (verbose == 1) {
+          printf("%s.", dns_dntosp(dn));
+          printf(" CNAME %s.\n", dns_dntosp(p.dnsp_dnbuf));
+        }
+        dns_dntodn(p.dnsp_dnbuf, dn, sizeof(dn));
+      }
+    }
+  }
+  if (!r && !nrr)
+    r = DNS_E_NODATA;
+  if (r < 0) {
+    dnserror(q, r);
+    free(result);
+    return;
+  }
+  if (verbose < 2) {   /* else it is already printed by dbgfn */
+    dns_rewind(&p, NULL);
+    p.dnsp_qtyp = q->qtyp == DNS_T_ANY ? 0 : q->qtyp;
+    p.dnsp_qcls = qcls == DNS_C_ANY ? 0 : qcls;
+    while(dns_nextrr(&p, &rr))
+      printrr(&p, &rr);
+  }
+  free(result);
+  query_free(q);
+}
+
+int main(int argc, char **argv) {
+  int i;
+  int fd;
+  fd_set fds;
+  struct timeval tv;
+  time_t now;
+  char *ns[DNS_MAXSERV];
+  int nns = 0;
+  struct query *q;
+  enum dns_type qtyp = 0;
+  struct dns_ctx *nctx = NULL;
+  int flags = 0;
+
+  if (!(progname = strrchr(argv[0], '/'))) progname = argv[0];
+  else argv[0] = ++progname;
+
+  if (argc <= 1)
+    die(0, "try `%s -h' for help", progname);
+
+  if (dns_init(NULL, 0) < 0 || !(nctx = dns_new(NULL)))
+    die(errno, "unable to initialize dns library");
+  /* we keep two dns contexts: one may be needed to resolve
+   * nameservers if given as names, using default options.
+   */
+
+  while((i = getopt(argc, argv, "vqt:c:an:o:f:h")) != EOF) switch(i) {
+  case 'v': ++verbose; break;
+  case 'q': --verbose; break;
+  case 't':
+    if (optarg[0] == '*' && !optarg[1])
+      i = DNS_T_ANY;
+    else if ((i = dns_findtypename(optarg)) <= 0)
+      die(0, "unrecognized query type `%s'", optarg);
+    qtyp = i;
+    break;
+  case 'c':
+    if (optarg[0] == '*' && !optarg[1])
+      i = DNS_C_ANY;
+    else if ((i = dns_findclassname(optarg)) < 0)
+      die(0, "unrecognized query class `%s'", optarg);
+    qcls = i;
+    break;
+  case 'a':
+    qtyp = DNS_T_ANY;
+    ++verbose;
+    break;
+  case 'n':
+    if (nns >= DNS_MAXSERV)
+      die(0, "too many nameservers, %d max", DNS_MAXSERV);
+    ns[nns++] = optarg;
+    break;
+  case 'o':
+  case 'f': {
+    char *opt;
+    const char *const delim = " \t,;";
+    for(opt = strtok(optarg, delim); opt != NULL; opt = strtok(NULL, delim)) {
+      if (dns_set_opts(NULL, optarg) == 0)
+        ;
+      else if (strcmp(opt, "aa") == 0) flags |= DNS_AAONLY;
+      else if (strcmp(optarg, "nord") == 0) flags |= DNS_NORD;
+      else if (strcmp(optarg, "dnssec") == 0) flags |= DNS_SET_DO;
+      else if (strcmp(optarg, "do")     == 0) flags |= DNS_SET_DO;
+      else if (strcmp(optarg, "cd") == 0) flags |= DNS_SET_CD;
+      else
+        die(0, "invalid option: `%s'", opt);
+    }
+    break;
+  }
+  case 'h':
+    printf(
+"%s: simple DNS query tool (using udns version %s)\n"
+"Usage: %s [options] domain-name...\n"
+"where options are:\n"
+" -h - print this help and exit\n"
+" -v - be more verbose\n"
+" -q - be less verbose\n"
+" -t type - set query type (A, AAA, PTR etc)\n"
+" -c class - set query class (IN (default), CH, HS, *)\n"
+" -a - equivalent to -t ANY -v\n"
+" -n ns - use given nameserver(s) instead of default\n"
+"  (may be specified multiple times)\n"
+" -o opt,opt,... (comma- or space-separated list,\n"
+"                 may be specified more than once):\n"
+"  set resovler options (the same as setting $RES_OPTIONS):\n"
+"   timeout:sec  - initial query timeout\n"
+"   attempts:num - number of attempt to resovle a query\n"
+"   ndots:num    - if name has more than num dots, lookup it before search\n"
+"   port:num     - port number for queries instead of default 53\n"
+"   udpbuf:num   - size of UDP buffer (use EDNS0 if >512)\n"
+"  or query flags:\n"
+"   aa,nord,dnssec,do,cd - set query flag (auth-only, no recursion,\n"
+"     enable DNSSEC (DNSSEC Ok), check disabled)\n"
+      , progname, dns_version(), progname);
+    return 0;
+  default:
+    die(0, "try `%s -h' for help", progname);
+  }
+
+  argc -= optind; argv += optind;
+  if (!argc)
+    die(0, "no name(s) to query specified");
+
+  if (nns) {
+    /* if nameservers given as names, resolve them.
+     * We only allow IPv4 nameservers as names for now.
+     * Ok, it is easy enouth to try both AAAA and A,
+     * but the question is what to do by default.
+     */
+    struct sockaddr_in sin;
+    int j, r = 0, opened = 0;
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(dns_set_opt(NULL, DNS_OPT_PORT, -1));
+    dns_add_serv(NULL, NULL);
+    for(i = 0; i < nns; ++i) {
+      if (dns_pton(AF_INET, ns[i], &sin.sin_addr) <= 0) {
+        struct dns_rr_a4 *rr;
+        if (!opened) {
+          if (dns_open(nctx) < 0)
+            die(errno, "unable to initialize dns context");
+          opened = 1;
+        }
+        rr = dns_resolve_a4(nctx, ns[i], 0);
+        if (!rr)
+          die(0, "unable to resolve nameserver %s: %s",
+              ns[i], dns_strerror(dns_status(nctx)));
+        for(j = 0; j < rr->dnsa4_nrr; ++j) {
+          sin.sin_addr = rr->dnsa4_addr[j];
+          if ((r = dns_add_serv_s(NULL, (struct sockaddr *)&sin)) < 0)
+            break;
+        }
+        free(rr);
+      }
+      else
+        r = dns_add_serv_s(NULL, (struct sockaddr *)&sin);
+      if (r < 0)
+        die(errno, "unable to add nameserver %s",
+             dns_xntop(AF_INET, &sin.sin_addr));
+    }
+  }
+  dns_free(nctx);
+
+  fd = dns_open(NULL);
+  if (fd < 0)
+    die(errno, "unable to initialize dns context");
+
+  if (verbose > 1)
+    dns_set_dbgfn(NULL, dbgcb);
+
+  if (flags)
+    dns_set_opt(NULL, DNS_OPT_FLAGS, flags);
+
+  for (i = 0; i < argc; ++i) {
+    char *name = argv[i];
+    union {
+      struct in_addr addr;
+      struct in6_addr addr6;
+    } a;
+    unsigned char dn[DNS_MAXDN];
+    enum dns_type l_qtyp = 0;
+    int abs;
+    if (dns_pton(AF_INET, name, &a.addr) > 0) {
+      dns_a4todn(&a.addr, 0, dn, sizeof(dn));
+      l_qtyp = DNS_T_PTR;
+      abs = 1;
+    }
+#ifdef HAVE_IPv6
+    else if (dns_pton(AF_INET6, name, &a.addr6) > 0) {
+      dns_a6todn(&a.addr6, 0, dn, sizeof(dn));
+      l_qtyp = DNS_T_PTR;
+      abs = 1;
+    }
+#endif
+    else if (!dns_ptodn(name, strlen(name), dn, sizeof(dn), &abs))
+      die(0, "invalid name `%s'\n", name);
+    else
+      l_qtyp = DNS_T_A;
+    if (qtyp) l_qtyp = qtyp;
+    q = query_new(name, dn, l_qtyp);
+    if (abs) abs = DNS_NOSRCH;
+    if (!dns_submit_dn(NULL, dn, qcls, l_qtyp, abs, 0, dnscb, q))
+      dnserror(q, dns_status(NULL));
+  }
+
+  FD_ZERO(&fds);
+  now = 0;
+  while((i = dns_timeouts(NULL, -1, now)) > 0) {
+    FD_SET(fd, &fds);
+    tv.tv_sec = i;
+    tv.tv_usec = 0;
+    i = select(fd+1, &fds, 0, 0, &tv);
+    now = time(NULL);
+    if (i > 0) dns_ioevent(NULL, now);
+  }
+
+  return errors ? 1 : notfound ? 100 : 0;
+}
diff --git a/3rdparty/udns/ex-rdns.c b/3rdparty/udns/ex-rdns.c
new file mode 100644 (file)
index 0000000..1e1e90d
--- /dev/null
@@ -0,0 +1,114 @@
+/* ex-rdns.c
+   parallel rDNS resolver example - read IP addresses from stdin,
+   write domain names to stdout
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/poll.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include "udns.h"
+
+static int curq;
+
+static const char *n2ip(const unsigned char *c) {
+  static char b[sizeof("255.255.255.255")];
+  sprintf(b, "%u.%u.%u.%u", c[0], c[1], c[2], c[3]);
+  return b;
+}
+static void dnscb(struct dns_ctx *ctx, struct dns_rr_ptr *rr, void *data) {
+  const char *ip = n2ip((unsigned char *)&data);
+  int i;
+  --curq;
+  if (rr) {
+    printf("%s", ip);
+    for(i = 0; i < rr->dnsptr_nrr; ++i)
+      printf(" %s", rr->dnsptr_ptr[i]);
+    putchar('\n');
+    free(rr);
+  }
+  else
+    fprintf(stderr, "%s: %s\n", ip, dns_strerror(dns_status(ctx)));
+}
+
+int main(int argc, char **argv) {
+  int c;
+  time_t now;
+  int maxq = 10;
+  struct pollfd pfd;
+  char linebuf[1024];
+  char *eol;
+  int eof;
+
+  if (dns_init(NULL, 1) < 0) {
+    fprintf(stderr, "unable to initialize dns library\n");
+    return 1;
+  }
+  while((c = getopt(argc, argv, "m:r")) != EOF) switch(c) {
+  case 'm': maxq = atoi(optarg); break;
+  case 'r':
+     dns_set_opt(0, DNS_OPT_FLAGS,
+                 dns_set_opt(0, DNS_OPT_FLAGS, -1) | DNS_NORD);
+     break;
+  default: return 1;
+  }
+  if (argc != optind) return 1;
+
+  pfd.fd = dns_sock(0);
+  pfd.events = POLLIN;
+  now = time(NULL);
+  c = optind;
+  eof = 0;
+  while(curq || !eof) {
+    if (!eof && curq < maxq) {
+      union { struct in_addr a; void *p; } pa;
+      if (!fgets(linebuf, sizeof(linebuf), stdin)) {
+        eof = 1;
+        continue;
+      }
+      eol = strchr(linebuf, '\n');
+      if (eol) *eol = '\0';
+      if (!linebuf[0]) continue;
+      if (dns_pton(AF_INET, linebuf, &pa.a) <= 0)
+        fprintf(stderr, "%s: invalid address\n", linebuf);
+      else if (dns_submit_a4ptr(0, &pa.a, dnscb, pa.p) == 0)
+        fprintf(stderr, "%s: unable to submit query: %s\n",
+                linebuf, dns_strerror(dns_status(0)));
+      else
+        ++curq;
+      continue;
+    }
+    if (curq) {
+      c = dns_timeouts(0, -1, now);
+      c = poll(&pfd, 1, c < 0 ? -1 : c * 1000);
+      now = time(NULL);
+      if (c)
+        dns_ioevent(0, now);
+    }
+  }
+  return 0;
+}
diff --git a/3rdparty/udns/getopt.c b/3rdparty/udns/getopt.c
new file mode 100644 (file)
index 0000000..e15a7a0
--- /dev/null
@@ -0,0 +1,165 @@
+/* getopt.c
+ * Simple getopt() implementation.
+ *
+ * Standard interface:
+ *  extern int getopt(int argc, char *const *argv, const char *opts);
+ *  extern int optind;    current index in argv[]
+ *  extern char *optarg;  argument for the current option
+ *  extern int optopt;    the current option
+ *  extern int opterr;    to control error printing
+ *
+ * Some minor extensions:
+ *  ignores leading `+' sign in opts[] (unemplemented GNU extension)
+ *  handles optional arguments, in form "x::" in opts[]
+ *  if opts[] starts with `:', will return `:' in case of missing required
+ *    argument, instead of '?'.
+ *
+ * Compile with -DGETOPT_NO_OPTERR to never print errors internally.
+ * Compile with -DGETOPT_NO_STDIO to use write() calls instead of fprintf() for
+ *  error reporting (ignored with -DGETOPT_NO_OPTERR).
+ * Compile with -DGETOPT_CLASS=static to get static linkage.
+ * Compile with -DGETOPT_MY to redefine all visible symbols to be prefixed
+ *  with "my_", like my_getopt instead of getopt.
+ * Compile with -DTEST to get a test executable.
+ *
+ * Written by Michael Tokarev.  Public domain.
+ */
+
+#include <string.h>
+
+#ifndef GETOPT_CLASS
+# define GETOPT_CLASS
+#endif
+#ifdef GETOPT_MY
+# define optarg my_optarg
+# define optind my_optind
+# define opterr my_opterr
+# define optopt my_optopt
+# define getopt my_getopt
+#endif
+
+GETOPT_CLASS char *optarg /* = NULL */;
+GETOPT_CLASS int optind = 1;
+GETOPT_CLASS int opterr = 1;
+GETOPT_CLASS int optopt;
+
+static char *nextc /* = NULL */;
+
+#if defined(GETOPT_NO_OPTERR)
+
+#define printerr(argv, msg)
+
+#elif defined(GETOPT_NO_STDIO)
+
+extern int write(int, void *, int);
+
+static void printerr(char *const *argv, const char *msg) {
+  if (opterr) {
+    char buf[64];
+    unsigned pl = strlen(argv[0]);
+    unsigned ml = strlen(msg);
+    char *p;
+    if (pl + /*": "*/2 + ml + /*" -- c\n"*/6 > sizeof(buf)) {
+      write(2, argv[0], pl);
+      p = buf;
+    }
+    else {
+      memcpy(buf, argv[0], ml);
+      p = buf + pl;
+    }
+    *p++ = ':'; *p++ = ' ';
+    memcpy(p, msg, ml); p += ml;
+    *p++ = ' '; *p++ = '-'; *p++ = '-'; *p++ = ' ';
+    *p++ = optopt;
+    *p++ = '\n';
+    write(2, buf, p - buf);
+  }
+}
+
+#else
+
+#include <stdio.h>
+static void printerr(char *const *argv, const char *msg) {
+  if (opterr)
+     fprintf(stderr, "%s: %s -- %c\n", argv[0], msg, optopt);
+}
+
+#endif
+
+GETOPT_CLASS int getopt(int argc, char *const *argv, const char *opts) {
+  char *p;
+
+  optarg = 0;
+  if (*opts == '+') /* GNU extension (permutation) - isn't supported */
+    ++opts;
+
+  if (!optind) {  /* a way to reset things */
+    nextc = 0;
+    optind = 1;
+  }
+
+  if (!nextc || !*nextc) {   /* advance to the next argv element */
+    /* done scanning? */
+    if (optind >= argc)
+      return -1;
+    /* not an optional argument */
+    if (argv[optind][0] != '-')
+      return -1;
+    /* bare `-' */
+    if (argv[optind][1] == '\0')
+      return -1;
+    /* special case `--' argument */
+    if (argv[optind][1] == '-' && argv[optind][2] == '\0') {
+      ++optind;
+      return -1;
+    }
+    nextc = argv[optind] + 1;
+  }
+
+  optopt = *nextc++;
+  if (!*nextc)
+    ++optind;
+  p = strchr(opts, optopt);
+  if (!p || optopt == ':') {
+    printerr(argv, "illegal option");
+    return '?';
+  }
+  if (p[1] == ':') {
+    if (*nextc) {
+      optarg = nextc;
+      nextc = NULL;
+      ++optind;
+    }
+    else if (p[2] != ':') {    /* required argument */
+      if (optind >= argc) {
+        printerr(argv, "option requires an argument");
+        return *opts == ':' ? ':' : '?';
+      }
+      else
+        optarg = argv[optind++];
+    }
+  }
+  return optopt;
+}
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+  int c;
+  while((c = getopt(argc, argv, "ab:c::")) != -1) switch(c) {
+  case 'a':
+  case 'b':
+  case 'c':
+    printf("option %c %s\n", c, optarg ? optarg : "(none)");
+    break;
+  default:
+    return -1;
+  }
+  for(c = optind; c < argc; ++c)
+    printf("non-opt: %s\n", argv[c]);
+  return 0;
+}
+
+#endif
diff --git a/3rdparty/udns/inet_XtoX.c b/3rdparty/udns/inet_XtoX.c
new file mode 100644 (file)
index 0000000..50b5f8e
--- /dev/null
@@ -0,0 +1,327 @@
+/* inet_XtoX.c
+ * Simple implementation of the following functions:
+ *  inet_ntop(), inet_ntoa(), inet_pton(), inet_aton().
+ *
+ * Differences from traditional implementaitons:
+ *  o modifies destination buffers even on error return.
+ *  o no fancy (hex, or 1.2) input support in inet_aton()
+ *  o inet_aton() does not accept junk after an IP address.
+ *  o inet_ntop(AF_INET) requires at least 16 bytes in dest,
+ *    and inet_ntop(AF_INET6) at least 40 bytes
+ *    (traditional inet_ntop() will try to fit anyway)
+ *
+ * Compile with -Dinet_XtoX_prefix=pfx_ to have pfx_*() instead of inet_*()
+ * Compile with -Dinet_XtoX_no_ntop or -Dinet_XtoX_no_pton
+ *  to disable net2str or str2net conversions.
+ *
+ * #define inet_XtoX_prototypes and #include "this_file.c"
+ * to get function prototypes only (but not for inet_ntoa()).
+ * #define inet_XtoX_decl to be `static' for static visibility,
+ * or use __declspec(dllexport) or somesuch...
+ *
+ * Compile with -DTEST to test against stock implementation.
+ *
+ * Written by Michael Tokarev.  Public domain.
+ */
+
+#ifdef inet_XtoX_prototypes
+
+struct in_addr;
+
+#else
+
+#include <errno.h>
+
+#ifdef TEST
+
+# include <netinet/in.h>
+# include <sys/socket.h>
+# include <arpa/inet.h>
+# include <stdio.h>
+# include <stdlib.h>
+# include <unistd.h>
+# include <string.h>
+# undef inet_XtoX_prefix
+# define inet_XtoX_prefix mjt_inet_
+# undef inet_XtoX_no_ntop
+# undef inet_XtoX_no_pton
+
+#else /* !TEST */
+
+struct in_addr {       /* declare it here to avoid messing with headers */
+  unsigned char x[4];
+};
+
+#endif /* TEST */
+
+#endif /* inet_XtoX_prototypes */
+
+#ifndef inet_XtoX_prefix
+# define inet_XtoX_prefix inet_
+#endif
+#ifndef inet_XtoX_decl
+# define inet_XtoX_decl /*empty*/
+#endif
+
+#define cc2_(x,y) cc2__(x,y)
+#define cc2__(x,y) x##y
+#define fn(x) cc2_(inet_XtoX_prefix,x)
+
+#ifndef inet_XtoX_no_ntop
+
+inet_XtoX_decl const char *
+fn(ntop)(int af, const void *src, char *dst, unsigned size);
+
+#ifndef inet_XtoX_prototypes
+
+static int mjt_ntop4(const void *_src, char *dst, int size) {
+  unsigned i, x, r;
+  char *p;
+  const unsigned char *s = _src;
+  if (size < 4*4)      /* for simplicity, disallow non-max-size buffer */
+    return 0;
+  for (i = 0, p = dst; i < 4; ++i) {
+    if (i) *p++ = '.';
+    x = r = s[i];
+    if (x > 99) { *p++ = (char)(r / 100 + '0'); r %= 100; }
+    if (x > 9) { *p++ = (char)(r / 10 + '0'); r %= 10; }
+    *p++ = (char)(r + '0');
+  }
+  *p = '\0';
+  return 1;
+}
+
+static char *hexc(char *p, unsigned x) {
+  static char hex[16] = "0123456789abcdef";
+  if (x > 0x0fff) *p++ = hex[(x >>12) & 15];
+  if (x > 0x00ff) *p++ = hex[(x >> 8) & 15];
+  if (x > 0x000f) *p++ = hex[(x >> 4) & 15];
+  *p++ = hex[x & 15];
+  return p;
+}
+
+static int mjt_ntop6(const void *_src, char *dst, int size) {
+  unsigned i;
+  unsigned short w[8];
+  unsigned bs = 0, cs = 0;
+  unsigned bl = 0, cl = 0;
+  char *p;
+  const unsigned char *s = _src;
+
+  if (size < 40)       /* for simplicity, disallow non-max-size buffer */
+    return 0;
+
+  for(i = 0; i < 8; ++i, s += 2) {
+    w[i] = (((unsigned short)(s[0])) << 8) | s[1];
+    if (!w[i]) {
+      if (!cl++) cs = i;
+    }
+    else {
+      if (cl > bl) bl = cl, bs = cs;
+    }
+  }
+  if (cl > bl) bl = cl, bs = cs;
+  p = dst;
+  if (bl == 1)
+    bl = 0;
+  if (bl) {
+    for(i = 0; i < bs; ++i) {
+      if (i) *p++ = ':';
+      p = hexc(p, w[i]);
+    }
+    *p++ = ':';
+    i += bl;
+    if (i == 8)
+      *p++ = ':';
+  }
+  else
+    i = 0;
+  for(; i < 8; ++i) {
+    if (i) *p++ = ':';
+    if (i == 6 && !bs && (bl == 6 || (bl == 5 && w[5] == 0xffff)))
+      return mjt_ntop4(s - 4, p, size - (p - dst));
+    p = hexc(p, w[i]);
+  }
+  *p = '\0';
+  return 1;
+}
+
+inet_XtoX_decl const char *
+fn(ntop)(int af, const void *src, char *dst, unsigned size) {
+  switch(af) {
+  /* don't use AF_*: don't mess with headers */
+  case 2:  /* AF_INET */  if (mjt_ntop4(src, dst, size)) return dst; break;
+  case 10: /* AF_INET6 */ if (mjt_ntop6(src, dst, size)) return dst; break;
+  default: errno = EAFNOSUPPORT; return (char*)0;
+  }
+  errno = ENOSPC;
+  return (char*)0;
+}
+
+inet_XtoX_decl const char *
+fn(ntoa)(struct in_addr addr) {
+  static char buf[4*4];
+  mjt_ntop4(&addr, buf, sizeof(buf));
+  return buf;
+}
+
+#endif /* inet_XtoX_prototypes */
+#endif /* inet_XtoX_no_ntop */
+
+#ifndef inet_XtoX_no_pton
+
+inet_XtoX_decl int fn(pton)(int af, const char *src, void *dst);
+inet_XtoX_decl int fn(aton)(const char *src, struct in_addr *addr);
+
+#ifndef inet_XtoX_prototypes
+
+static int mjt_pton4(const char *c, void *dst) {
+  unsigned char *a = dst;
+  unsigned n, o;
+  for (n = 0; n < 4; ++n) {
+    if (*c < '0' || *c > '9')
+      return 0;
+    o = *c++ - '0';
+    while(*c >= '0' && *c <= '9')
+      if ((o = o * 10 + (*c++ - '0')) > 255)
+        return 0;
+    if (*c++ != (n == 3 ? '\0' : '.'))
+      return 0;
+    *a++ = (unsigned char)o;
+  }
+  return 1;
+}
+
+static int mjt_pton6(const char *c, void *dst) {
+  unsigned short w[8], *a = w, *z, *i;
+  unsigned v, o;
+  const char *sc;
+  unsigned char *d = dst;
+  if (*c != ':') z = (unsigned short*)0;
+  else if (*++c != ':') return 0;
+  else ++c, z = a;
+  i = 0;
+  for(;;) {
+    v = 0;
+    sc = c;
+    for(;;) {
+      if (*c >= '0' && *c <= '9') o = *c - '0';
+      else if (*c >= 'a' && *c <= 'f') o = *c - 'a' + 10;
+      else if (*c >= 'A' && *c <= 'F') o = *c - 'A' + 10;
+      else break;
+      v = (v << 4) | o;
+      if (v > 0xffff) return 0;
+      ++c;
+    }
+    if (sc == c) {
+      if (z == a && !*c)
+        break;
+      else
+        return 0;
+    }
+    if (*c == ':') {
+      if (a >= w + 8)
+        return 0;
+      *a++ = v;
+      if (*++c == ':') {
+        if (z)
+          return 0;
+        z = a;
+        if (!*++c)
+          break;
+      }
+    }
+    else if (!*c) {
+      if (a >= w + 8)
+        return 0;
+      *a++ = v;
+      break;
+    }
+    else if (*c == '.') {
+      if (a > w + 6)
+        return 0;
+      if (!mjt_pton4(sc, d))
+        return 0;
+      *a++ = ((unsigned)(d[0]) << 8) | d[1];
+      *a++ = ((unsigned)(d[2]) << 8) | d[3];
+      break;
+    }
+    else
+      return 0;
+  }
+  v = w + 8 - a;
+  if ((v && !z) || (!v && z))
+    return 0;
+  for(i = w; ; ++i) {
+    if (i == z)
+      while(v--) { *d++ = '\0'; *d++ = '\0'; }
+    if (i >= a)
+      break;
+    *d++ = (unsigned char)((*i >> 8) & 255);
+    *d++ = (unsigned char)(*i & 255);
+  }
+  return 1;
+}
+
+inet_XtoX_decl int fn(pton)(int af, const char *src, void *dst) {
+  switch(af) {
+  /* don't use AF_*: don't mess with headers */
+  case 2  /* AF_INET  */: return mjt_pton4(src, dst);
+  case 10 /* AF_INET6 */: return mjt_pton6(src, dst);
+  default: errno = EAFNOSUPPORT; return -1;
+  }
+}
+
+inet_XtoX_decl int fn(aton)(const char *src, struct in_addr *addr) {
+  return mjt_pton4(src, addr);
+}
+
+#endif /* inet_XtoX_prototypes */
+
+#endif /* inet_XtoX_no_pton */
+
+#ifdef TEST
+
+int main(int argc, char **argv) {
+  int i;
+  char n0[16], n1[16];
+  char p0[64], p1[64];
+  int af = AF_INET;
+  int pl = sizeof(p0);
+  int r0, r1;
+  const char *s0, *s1;
+
+  while((i = getopt(argc, argv, "46a:p:")) != EOF) switch(i) {
+  case '4': af = AF_INET;  break;
+  case '6': af = AF_INET6; break;
+  case 'a': case 'p': pl = atoi(optarg); break;
+  default: return 1;
+  }
+  for(i = optind; i < argc; ++i) {
+    char *a = argv[i];
+
+    printf("%s:\n", a);
+    r0 = inet_pton(af, a, n0);
+    printf(" p2n stock: %s\n",
+     (r0 < 0 ? "(notsupp)" : !r0 ? "(inval)" : fn(ntop)(af,n0,p0,sizeof(p0))));
+    r1 = fn(pton)(af, a, n1);
+    printf(" p2n this : %s\n",
+     (r1 < 0 ? "(notsupp)" : !r1 ? "(inval)" : fn(ntop)(af,n1,p1,sizeof(p1))));
+
+    if ((r0 > 0) != (r1 > 0) ||
+        (r0 > 0 && r1 > 0 && memcmp(n0, n1, af == AF_INET ? 4 : 16) != 0))
+      printf(" DIFFER!\n");
+
+    s0 = inet_ntop(af, n1, p0, pl);
+    printf(" n2p stock: %s\n", s0 ? s0 : "(inval)");
+    s1 = fn(ntop)(af, n1, p1, pl);
+    printf(" n2p this : %s\n", s1 ? s1 : "(inval)");
+    if ((s0 != 0) != (s1 != 0) ||
+        (s0 && s1 && strcmp(s0, s1) != 0))
+      printf(" DIFFER!\n");
+
+  }
+  return 0;
+}
+
+#endif /* TEST */
diff --git a/3rdparty/udns/rblcheck.1 b/3rdparty/udns/rblcheck.1
new file mode 100644 (file)
index 0000000..6c822c0
--- /dev/null
@@ -0,0 +1,151 @@
+.\" rblcheck.1
+.\" rblckeck manpage
+.\"
+.\" Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+.\" This file is part of UDNS library, an async DNS stub resolver.
+.\"
+.\" This library is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU Lesser General Public
+.\" License as published by the Free Software Foundation; either
+.\" version 2.1 of the License, or (at your option) any later version.
+.\"
+.\" This library 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
+.\" Lesser General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU Lesser General Public
+.\" License along with this library, in file named COPYING.LGPL; if not,
+.\" write to the Free Software Foundation, Inc., 59 Temple Place,
+.\" Suite 330, Boston, MA  02111-1307  USA
+
+.TH rblckeck 1 "Apr 2005" "User Utilities"
+
+.SH NAME
+rblckeck \- DNSBL lookup utility
+
+.SH SYNOPSYS
+.B rblcheck
+.RB [\| \-s
+.IR zone \|]
+.RB [\| \-S
+.IR zone\-file \|]
+.RB [\| \-c \|]
+.RB [\| \-tmvq \|]
+.RB [\| \-n
+.IR nsaddr \|]
+.IR address \|.\|.\|.
+
+.SH DESCRIPTION
+.B rblcheck
+is a simple command-line to perform DNSBL (DNS-based blocklists) lookups.
+For every IP address (or a name, in which case it will be resolved to an
+address first), the utility verifies whenever it is listed in a (list of)
+DNS blocklists specified with
+.B \-s
+or
+.B \-S
+options, optionally obtains text assotiated with the listing (usually it
+is either some description about the reason of the listing or an URL
+referring to such a description), and displays results on standard output.
+.PP
+The program is implemented on top of
+.BR udns (3)
+library.
+
+.SH OPTIONS
+
+The following options are recognized by
+.BR rblcheck :
+
+.TP
+.B \-s \fIzone\fR
+add the given \fIzone\fR DNSBL name to the list of active zones.
+.TP
+.B \-S \fIzone-file\fR
+add list of zones from the named \fIzone-file\fR to the list of
+active zones (the file specifies one zone as the first word on a
+line, empty lines and lines starting with `#' character are ignored).
+.TP
+.B \-c
+reset active zone list.
+.TP
+.B \-v
+be more verbose, produce more detailed output.
+.TP
+.B \-q
+the opposite for \fB\-v\fR -- produce less detailed output.
+.TP
+.B \-t
+obtain text for listed addresses.
+.TP
+.B \-n \fInsaddr\fR
+Use the given nameserver (given as IPv4 or IPv6 address) instead of the
+default.  The same effect may be achieved by setting $NSCACHEIP environment
+variable.
+.TP
+.B \-m
+stop after first hit, ie after the first address which is found to be
+listed.
+
+.TP
+.B \-h
+print short help and exit.
+
+.PP
+If no
+.BR \-s ,
+.BR \-S
+and
+.B \-c
+options are given,
+.B rblcheck
+will try to obtain list of zones using $RBLCHECK_ZONES environment variable,
+or ~/.rblcheckrc, or /etc/rblckechrc files, in that order.  If no zones are
+found, it will exit unsuccessefully.
+
+.SH "RETURN VALUE"
+When no addresses given are listed and no errors occured,
+.B rblcheck
+exits with code 0.  If at least one address is listed,
+.B rblcheck
+returns 100.  In case of DNS errors,
+.B rblcheck
+returns 2.
+
+.SH ENVIRONMENT
+
+.TP
+.B $RBLCHECK_ZONES
+if no
+.BR \-s ,
+.B \-S
+or
+.B \-c
+option is given,
+.B rblcheck
+tries this variable to obtain list of DNSBL zones to check against.
+
+.SH FILES
+
+.TP
+$HOME/.rblcheckrc and /etc/rblcheckrc
+if no
+.BR \-s ,
+.B \-S
+or
+.B \-c
+option is given, and no $RBLCHECK_ZONES environment variable is set,
+.B rblcheck
+will try the two files (the first one that exists) to obtain list of
+DNSBL zones to check against.
+Each line specifies one zone (only first word in each line is used).
+Empty lines and lines starting with `#' character are ignored.
+
+.SH "SEE ALSO"
+.BR dnsget (1)
+.BR resolv.conf (5)
+.BR udns (3).
+
+.SH AUTHOR
+This program and manual pages are written by Michael Tokarev.
diff --git a/3rdparty/udns/rblcheck.c b/3rdparty/udns/rblcheck.c
new file mode 100644 (file)
index 0000000..82d29de
--- /dev/null
@@ -0,0 +1,378 @@
+/* rblcheck.c
+   dnsbl (rbl) checker application
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef WINDOWS
+# include <winsock2.h>
+#else
+# include <unistd.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+#endif
+#include <time.h>
+#include <errno.h>
+#include <stdarg.h>
+#include "udns.h"
+
+#ifndef HAVE_GETOPT
+# include "getopt.c"
+#endif
+
+static const char *version = "udns-rblcheck 0.4";
+static char *progname;
+
+static void error(int die, const char *fmt, ...) {
+  va_list ap;
+  fprintf(stderr, "%s: ", progname);
+  va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);
+  putc('\n', stderr);
+  fflush(stderr);
+  if (die)
+    exit(1);
+}
+
+struct rblookup {
+  struct ipcheck *parent;
+  struct in_addr key;
+  const char *zone;
+  struct dns_rr_a4  *addr;
+  struct dns_rr_txt *txt;
+};
+
+struct ipcheck {
+  const char *name;
+  int naddr;
+  int listed;
+  struct rblookup *lookup;
+};
+
+#define notlisted ((void*)1)
+
+static int nzones, nzalloc;
+static const char **zones;
+
+static int do_txt;
+static int stopfirst;
+static int verbose = 1;
+/* verbosity level:
+ * <0 - only bare As/TXTs
+ * 0 - what RBL result
+ * 1(default) - what is listed by RBL: result
+ * 2          - what is[not ]listed by RBL: result, name lookups
+ */
+
+static int listed;
+static int failures;
+
+static void *ecalloc(int size, int cnt) {
+  void *t = calloc(size, cnt);
+  if (!t)
+    error(1, "out of memory");
+  return t;
+}
+
+static void addzone(const char *zone) {
+  if (nzones >= nzalloc) {
+    const char **zs = (const char**)ecalloc(sizeof(char*), (nzalloc += 16));
+    if (zones) {
+      memcpy(zs, zones, nzones * sizeof(char*));
+      free(zones);
+    }
+    zones = zs;
+  }
+  zones[nzones++] = zone;
+}
+
+static int addzonefile(const char *fname) {
+  FILE *f = fopen(fname, "r");
+  char linebuf[2048];
+  if (!f)
+    return 0;
+  while(fgets(linebuf, sizeof(linebuf), f)) {
+    char *p = linebuf, *e;
+    while(*p == ' ' || *p == '\t') ++p;
+    if (*p == '#' || *p == '\n') continue;
+    e = p;
+    while(*e && *e != ' ' && *e != '\t' && *e != '\n')
+      ++e;
+    *e++ = '\0';
+    p = memcpy(ecalloc(e - p, 1), p, e - p); // strdup
+    addzone(p);
+  }
+  fclose(f);
+  return 1;
+}
+
+static void dnserror(struct rblookup *ipl, const char *what) {
+  char buf[4*4];
+  error(0, "unable to %s for %s (%s): %s",
+          what, dns_ntop(AF_INET, &ipl->key, buf, sizeof(buf)),
+          ipl->zone, dns_strerror(dns_status(0)));
+  ++failures;
+}
+
+static void display_result(struct ipcheck *ipc) {
+  int j;
+  struct rblookup *l, *le;
+  char buf[4*4];
+  if (!ipc->naddr) return;
+  for (l = ipc->lookup, le = l + nzones * ipc->naddr; l < le; ++l) {
+    if (!l->addr) continue;
+    if (verbose < 2 && l->addr == notlisted) continue;
+    if (verbose >= 0) {
+      dns_ntop(AF_INET, &l->key, buf, sizeof(buf));
+      if (ipc->name) printf("%s[%s]", ipc->name, buf);
+      else printf("%s", buf);
+    }
+    if (l->addr == notlisted) {
+      printf(" is NOT listed by %s\n", l->zone);
+      continue;
+    }
+    else if (verbose >= 1)
+      printf(" is listed by %s: ", l->zone);
+    else if (verbose >= 0)
+      printf(" %s ", l->zone);
+    if (verbose >= 1 || !do_txt)
+      for (j = 0; j < l->addr->dnsa4_nrr; ++j)
+        printf("%s%s", j ? " " : "",
+               dns_ntop(AF_INET, &l->addr->dnsa4_addr[j], buf, sizeof(buf)));
+    if (!do_txt) ;
+    else if (l->txt) {
+      for(j = 0; j < l->txt->dnstxt_nrr; ++j) {
+        unsigned char *t = l->txt->dnstxt_txt[j].txt;
+        unsigned char *e = t + l->txt->dnstxt_txt[j].len;
+        printf("%s\"", verbose > 0 ? "\n\t" : j ? " " : "");
+        while(t < e) {
+          if (*t < ' ' || *t >= 127) printf("\\x%02x", *t);
+          else if (*t == '\\' || *t == '"') printf("\\%c", *t);
+          else putchar(*t);
+          ++t;
+        }
+        putchar('"');
+      }
+      free(l->txt);
+    }
+    else
+      printf("%s<no text available>", verbose > 0 ? "\n\t" : "");
+    free(l->addr);
+    putchar('\n');
+  }
+  free(ipc->lookup);
+}
+
+static void txtcb(struct dns_ctx *ctx, struct dns_rr_txt *r, void *data) {
+  struct rblookup *ipl = data;
+  if (r) {
+    ipl->txt = r;
+    ++ipl->parent->listed;
+  }
+  else if (dns_status(ctx) != DNS_E_NXDOMAIN)
+    dnserror(ipl, "lookup DNSBL TXT record");
+}
+
+static void a4cb(struct dns_ctx *ctx, struct dns_rr_a4 *r, void *data) {
+  struct rblookup *ipl = data;
+  if (r) {
+    ipl->addr = r;
+    ++listed;
+    if (do_txt) {
+      if (dns_submit_a4dnsbl_txt(0, &ipl->key, ipl->zone, txtcb, ipl))
+        return;
+      dnserror(ipl, "submit DNSBL TXT record");
+    }
+    ++ipl->parent->listed;
+  }
+  else if (dns_status(ctx) != DNS_E_NXDOMAIN)
+    dnserror(ipl, "lookup DNSBL A record");
+  else
+    ipl->addr = notlisted;
+}
+
+static int
+submit_a_queries(struct ipcheck *ipc,
+                 int naddr, const struct in_addr *addr) {
+  int z, a;
+  struct rblookup *rl = ecalloc(sizeof(*rl), nzones * naddr);
+  ipc->lookup = rl;
+  ipc->naddr = naddr;
+  for(a = 0; a < naddr; ++a) {
+    for(z = 0; z < nzones; ++z) {
+      rl->key = addr[a];
+      rl->zone = zones[z];
+      rl->parent = ipc;
+      if (!dns_submit_a4dnsbl(0, &rl->key, rl->zone, a4cb, rl))
+        dnserror(rl, "submit DNSBL A query");
+      ++rl;
+    }
+  }
+  return 0;
+}
+
+static void namecb(struct dns_ctx *ctx, struct dns_rr_a4 *rr, void *data) {
+  struct ipcheck *ipc = data;
+  if (rr) {
+    submit_a_queries(ipc, rr->dnsa4_nrr, rr->dnsa4_addr);
+    free(rr);
+  }
+  else {
+    error(0, "unable to lookup `%s': %s",
+          ipc->name, dns_strerror(dns_status(ctx)));
+    ++failures;
+  }
+}
+
+static int submit(struct ipcheck *ipc) {
+  struct in_addr addr;
+  if (dns_pton(AF_INET, ipc->name, &addr) > 0) {
+    submit_a_queries(ipc, 1, &addr);
+    ipc->name = NULL;
+  }
+  else if (!dns_submit_a4(0, ipc->name, 0, namecb, ipc)) {
+    error(0, "unable to submit name query for %s: %s\n",
+          ipc->name, dns_strerror(dns_status(0)));
+    ++failures;
+  }
+  return 0;
+}
+
+static void waitdns(struct ipcheck *ipc) {
+  struct timeval tv;
+  fd_set fds;
+  int c;
+  int fd = dns_sock(NULL);
+  time_t now = 0;
+  FD_ZERO(&fds);
+  while((c = dns_timeouts(NULL, -1, now)) > 0) {
+    FD_SET(fd, &fds);
+    tv.tv_sec = c;
+    tv.tv_usec = 0;
+    c = select(fd+1, &fds, NULL, NULL, &tv);
+    now = time(NULL);
+    if (c > 0)
+      dns_ioevent(NULL, now);
+    if (stopfirst && ipc->listed)
+      break;
+  }
+}
+
+int main(int argc, char **argv) {
+  int c;
+  struct ipcheck ipc;
+  char *nameserver = NULL;
+  int zgiven = 0;
+
+  if (!(progname = strrchr(argv[0], '/'))) progname = argv[0];
+  else argv[0] = ++progname;
+
+  while((c = getopt(argc, argv, "hqtvms:S:cn:")) != EOF) switch(c) {
+  case 's': ++zgiven; addzone(optarg); break;
+  case 'S':
+    ++zgiven;
+    if (addzonefile(optarg)) break;
+    error(1, "unable to read zonefile `%s'", optarg);
+  case 'c': ++zgiven; nzones = 0; break;
+  case 'q': --verbose; break;
+  case 'v': ++verbose; break;
+  case 't': do_txt = 1; break;
+  case 'n': nameserver = optarg; break;
+  case 'm': ++stopfirst; break;
+  case 'h':
+    printf("%s: %s (udns library version %s).\n",
+           progname, version, dns_version());
+    printf("Usage is: %s [options] address..\n", progname);
+    printf(
+"Where options are:\n"
+" -h - print this help and exit\n"
+" -s service - add the service (DNSBL zone) to the serice list\n"
+" -S service-file - add the DNSBL zone(s) read from the given file\n"
+" -c - clear service list\n"
+" -v - increase verbosity level (more -vs => more verbose)\n"
+" -q - decrease verbosity level (opposite of -v)\n"
+" -t - obtain and print TXT records if any\n"
+" -m - stop checking after first address match in any list\n"
+" -n ipaddr - use the given nameserver instead of the default\n"
+"(if no -s or -S option is given, use $RBLCHECK_ZONES, ~/.rblcheckrc\n"
+"or /etc/rblcheckrc in that order)\n"
+    );
+    return 0;
+  default:
+    error(1, "use `%s -h' for help", progname);
+  }
+
+  if (!zgiven) {
+    char *s = getenv("RBLCHECK_ZONES");
+    if (s) {
+      char *k;
+      s = strdup(s);
+      for(k = strtok(s, " \t"); k; k = strtok(NULL, " \t"))
+        addzone(k);
+      free(s);
+    }
+    else {     /* probably worthless on windows? */
+      char *path;
+      char *home = getenv("HOME");
+      if (!home) home = ".";
+      path = malloc(strlen(home) + 1 + sizeof(".rblcheckrc"));
+      sprintf(path, "%s/.rblcheckrc", home);
+      if (!addzonefile(path))
+        addzonefile("/etc/rblcheckrc");
+      free(path);
+    }
+  }
+  if (!nzones)
+    error(1, "no service (zone) list specified (-s or -S option)");
+
+  argv += optind;
+  argc -= optind;
+
+  if (!argc)
+    return 0;
+
+  if (dns_init(NULL, 0) < 0)
+    error(1, "unable to initialize DNS library: %s", strerror(errno));
+  if (nameserver) {
+    dns_add_serv(NULL, NULL);
+    if (dns_add_serv(NULL, nameserver) < 0)
+      error(1, "wrong IP address for a nameserver: `%s'", nameserver);
+  }
+  if (dns_open(NULL) < 0)
+    error(1, "unable to initialize DNS library: %s", strerror(errno));
+
+  for (c = 0; c < argc; ++c) {
+    if (c && (verbose > 1 || (verbose == 1 && do_txt))) putchar('\n');
+    memset(&ipc, 0, sizeof(ipc));
+    ipc.name = argv[c];
+    submit(&ipc);
+    waitdns(&ipc);
+    display_result(&ipc);
+    if (stopfirst > 1 && listed) break;
+  }
+
+  return listed ? 100 : failures ? 2 : 0;
+}
diff --git a/3rdparty/udns/udns.3 b/3rdparty/udns/udns.3
new file mode 100644 (file)
index 0000000..23222aa
--- /dev/null
@@ -0,0 +1,1352 @@
+.\" udns.3
+.\" udns library manpage
+.\"
+.\" Copyright (C) 2005-2014  Michael Tokarev <mjt+udns@tls.msk.ru>
+.\" This file is part of UDNS library, an async DNS stub resolver.
+.\"
+.\" This library is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU Lesser General Public
+.\" License as published by the Free Software Foundation; either
+.\" version 2.1 of the License, or (at your option) any later version.
+.\"
+.\" This library 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
+.\" Lesser General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU Lesser General Public
+.\" License along with this library, in file named COPYING.LGPL; if not,
+.\" write to the Free Software Foundation, Inc., 59 Temple Place,
+.\" Suite 330, Boston, MA  02111-1307  USA
+
+.TH udns 3 "Jan 2014" "Library Functions"
+
+.SH NAME
+udns \- stub DNS resolver library
+
+.SH SYNOPSYS
+.nf
+#include <udns.h>
+struct \fBdns_ctx\fR;
+struct \fBdns_query\fR;
+extern struct dns_ctx \fBdns_defctx\fR;
+struct dns_ctx *\fIctx\fR;
+typedef void \fBdns_query_fn\fR(\fIctx\fR, void *\fIresult\fR, void *\fIdata\fR);
+typedef int
+\fBdns_parse_fn\fR(const unsigned char *\fIqnd\fR,
+       const unsigned char *\fIpkt\fR,
+       const unsigned char *\fIcur\fR,
+       const unsigned char *\fIend\fR,
+       void **\fIresultp\fR);
+
+\fBcc\fR ... -l\fBudns\fR
+.fi
+
+.SH DESCRIPTION
+
+.PP
+The DNS library, \fBudns\fR, implements thread-safe stub DNS resolver
+functionality, which may be used both traditional, syncronous way
+and asyncronously, with application-supplied event loop.
+
+.PP
+While DNS works with both TCP and UDP, performing UDP query first and
+if the result does not fit in UDP buffer (512 bytes max for original
+DNS protocol), retrying the query over TCP, the library uses UDP only,
+but uses EDNS0 (RFC2671) extensions which allows larger UDP buffers.
+
+.PP
+The library uses single UDP socket to perform all operations even when
+asking multiple nameservers.  This way, it is very simple to use the
+library in asyncronous event-loop applications: an application should
+add only single socket to the set of filedescriptors it monitors for I/O.
+
+.PP
+The library uses two main objects, \fIresolver context\fR of type
+\fBstruct\ dns_ctx\fR, and \fIquery structure\fR of type
+\fBstruct\ dns_query\fR, both are opaque for an application.
+Resolver context holds global information about the resolver,
+such as list of nameservers to use, list of active requests and the like.
+Query objects holds information about a single DNS query in progress and
+are allocated/processed/freed by the library.   Pointer to query structure
+may be treated as an identifier of an in-progress query and may be used
+to cancel the asyncronous query or to wait for it to complete.
+
+.PP
+Asyncronous interface works as follows.  An application initializes
+resolver context, submits any number of queries for it using one of
+supplied \fBdns_submit_\fIXXX\fR() routines (each return the query
+identifier as pointer to query structure), waits for input on the
+UDP socket used by the library, and gives some control to the library
+by calling \fBdns_ioevent\fR() and \fBdns_timeouts\fR() routines when
+appropriate.  The library performs all necessary processing and executes
+application supplied callback routine when a query completes (either
+successefully or not), giving it the result if any, pointer to the
+resolver context (from which completion status may be obtained), and
+the data pointer supplied by an application when the query has been
+submitted.  When submitting a query, an application requests how to
+handle the reply -- to either return raw DNS reply packet for its
+own low-level processing, or it may provide an address of \fIparsing
+routine\fR of type \fBdns_parse_fn\fR to perform conversion of on-wire
+format into easy to use data structure (the library provides parsing
+routines for several commonly used resource record types, as well as
+type-safe higher-level inteface that requests parsing automatically).
+The I/O monitoring and timeout handling may be either traditional
+select() or poll() based, or any callback-driven technique may be
+used.
+
+.PP
+Additionally, the library provides traditional syncronous interface,
+which may be intermixed with asyncronous calls (during syncronous
+query processing, other asyncronous queries for the same resolver
+context continued to be processed as usual).  An application uses
+one of numerous \fBdns_resolve_\fIXXX\fR() routines provided by the
+library to perform a query.  As with asyncronous interface, an
+application may either request to return raw DNS packet or type-specific
+data structure by providing the parsing routine to handle the reply.
+Every routine from \fBdns_resolve_\fIXXX\fR() series return pointer
+to result or NULL in case of any error.  Query completion status
+(or length of the raw DNS packet) is available from the resolver
+context using \fBdns_status\fR() routine, the same way as for the
+asyncronous interface.
+
+.PP
+Internally, library uses on-wire format of domain names, referred
+to as \fIDN format\fR in this manual page.  This is a series of domain
+\fIlabels\fR whith preceeding length byte, terminated by zero-length
+label wich is integral part of the DN format.  There are several routines
+provided to convert from traditional asciiz string to DN and back.
+Higher-level type-specific query interface hides the DN format from
+an application.
+
+.SH "COMMON DEFINITIONS"
+
+.PP
+Every DNS Resource Record (RR) has a \fItype\fR and a \fIclass\fR.
+The library defines several integer constants, \fBDNS_C_\fIXXX\fR and
+\fBDNS_T_\fIXXX\fR, to use as symbolic names for RR classes and types,
+such as \fBDNS_C_IN\fR for Internet class, \fBDNS_T_A\fR for IPv4
+address record type and so on.  See udns.h header file for complete list
+of all such constants.
+
+.PP
+The following constants are defined in udns.h header file:
+.IP "\fBDNS_MAXDN\fR (255 bytes)"
+Maximum length of the domain name in internal (on-wire) DN format.
+.IP "\fBDNS_MAXLABEL\fR (63 bytes)"
+Maximum length of a single label in DN format.
+.IP "\fBDNS_MAXNAME\fR (1024 bytes)"
+Maximum length of asciiz format of a domain name.
+.IP "\fBDNS_HSIZE\fR (12 bytes)"
+Size of header in DNS packet.
+.IP "\fBDNS_PORT\fR (53)"
+Default port to use when contacting a DNS server.
+.IP "\fBDNS_MAXSERV\fR (6 servers)"
+Maximum number of DNS servers to use.
+.IP "\fBDNS_MAXPACKET\fR (512 bytes)"
+Maximum length of DNS UDP packet as specified by original DNS protocol
+.IP "\fBDNS_EDNS0PACKET\fR (4096 bytes)"
+Default length of DNS UDP packet (with EDNS0 extensions) the library uses.
+Note that recursive nameservers usually resides near the client asking them
+to resolve names, e.g. on the same LAN segment or even on the same host, so
+UDP packet fragmentation isn't a problem in most cases.  Note also that
+the size of actual packets will be as many bytes as actual reply size requires,
+which is smaller than this value in almost all cases.
+
+.PP
+Additionally, several constants are defined to simplify work with raw DNS
+packets, such as DNS response codes (\fBDNS_R_\fIXXX\fR), DNS header layout
+(\fBDNS_H_\fIXXX\fR) and others.  Again, see udns.h for complete list.
+Library error codes (\fBDNS_E_\fIXXX\fR) are described later in this
+manual page.
+
+.SH "RESOLVER CONTEXT"
+
+.PP
+Resolver context, of type \fBstruct\ dns_ctx\fR, is an object which is
+opaque to an application.  Several routines provided by the library
+to initialize, copy and free resolver contexts.  Most other high-level
+routines in this library expects a pointer to resolver context, \fIctx\fR,
+as the first argument.  There is a default resolver context available,
+named \fBdns_defctx\fR.  When the context pointer \fIctx\fR passed to
+a routine is NULL, \fBdns_defctx\fR is used.  Several resolver contexts
+may be active at the same time, for example, when an application is
+multi-threaded and each thread uses resolver.
+.PP
+In order to use the library, an application should initialize and open
+one or more resolver context objects.  These are two separate actions,
+performed by \fBdns_init\fR() (or \fBdns_reset\fR()), and \fBdns_open\fR().
+Between the two calls, an application is free to pefrorm additional
+initialisation, such as setting custom nameservers, options or domain search
+lists.  Optionally, in case no additional custom initialisation is required,
+\fBdns_init\fR() may open the context if \fIdo_open\fR argument (see below)
+is non-zero.
+.PP
+When initializing resolver context, the library uses information from
+system file /etc/resolv.conf (see \fBresolv.conf\fR(5)), consults
+environment variables \fB$LOCALDOMAIN\fR, \fB$NSCACHEIP\fR,
+\fB$NAMESERVERS\fR and \fB$RES_OPTIONS\fR, and local host name to obtain
+list of local nameservers, domain name search list and various resolver
+options.
+.PP
+The following routines to initialize resolver context are available:
+.PP
+.nf
+void \fBdns_reset\fR(\fIctx\fR)
+int \fBdns_init\fR(\fIctx\fR, int \fIdo_open\fR)
+.fi
+.RS
+\fBdns_reset\fR() resets a given resolver context to default values,
+preparing it to be opened by \fBdns_open\fR().
+It is ok to call this routine against opened and active context - all active
+queries will be dropped, sockets will be closed and so on.  This routine
+does not initialize any parameters from system configuration files, use
+\fBdns_init\fR() for this.  There's no error return - operation always
+succeeds.  \fBdns_init\fR() does everything \fBdns_reset\fR() does,
+plus initializes various parameters of the context according to system
+configuration and process environment variables.  If \fIdo_open\fR is
+non-zero, \fBdns_init\fR() calls \fIdns_open\fR(), so that the whole
+library initialisation is performed in a single step.
+.RE
+.PP
+.nf
+struct dns_ctx *\fBdns_new\fR(struct dns_ctx *\fIcopy\fR)
+void \fBdns_free\fR(\fIctx\fR)
+.fi
+.RS
+\fBdns_new\fR() allocates new resolver context and copies all parameters
+for a given resolver context \fIcopy\fR, or default context if \fIcopy\fR
+is NULL, and returns pointer to the newly allocated context.  The context
+being copied should be initialized.
+\fBdns_new\fR() may fail if there's no memory available to make a copy
+of \fIcopy\fR, in which case the routine will return NULL pointer.
+\fBdns_free\fR() is used to close assotiated socket and free resolver
+context resources and cancelling (abandoming) all active queries
+assotiated with it.  It's an error to free \fBdns_defctx\fR, only
+dynamically allocated contexts returned by \fBdns_new\fR() are allowed
+to be freed by \fBdns_free\fR().
+.RE
+.PP
+.nf
+int \fBdns_add_serv\fR(\fIctx\fR, const char *\fIservaddr\fR)
+int \fBdns_add_serv_s\fR(\fIctx\fR, const struct sockaddr *\fIsa\fR)
+int \fBdns_add_srch\fR(\fIctx\fR, const char *\fIsrch\fR)
+.fi
+.RS
+Add an element to list of nameservers (\fBdns_add_serv\fR(), as
+asciiz-string \fIservaddr\fR with an IP address of the nameserver,
+and \fBdns_add_serv_s\fR(), as initialized socket address \fIsa\fR),
+or search list (\fBdns_add_srch\fR(), as a pointer to domain name)
+for the given context \fIctx\fR.  If the last argument is a NULL
+pointer, the corresponding list (search or nameserver) is reset
+instead.  Upon successeful completion, each routine returns new
+number of elements in the list in question.  On error, negative
+value is returned and global variable \fBerrno\fR is set appropriately.
+It is an error to call any of this functions if the context is
+opened (after \fBdns_open\fR() or \fBdns_init\fR() with non-zero argument).
+.RE
+.PP
+.nf
+int \fBdns_set_opts\fR(\fIctx\fR, const char *\fIopts\fR)
+.fi
+.RS
+set resolver context options from \fIopts\fR string, in the same way as
+processing \fBoptions\fR statement in resolv.conf and \fB$RES_OPTIONS\fR
+environment variable.  Return number of unrecognized/invalid options
+found (all recognized and valid options gets processed).
+.RE
+.PP
+.nf
+void \fBdns_set_opt\fR(\fIctx\fR, int \fIopt\fR, \fIval\fR)
+.fi
+.RS
+.B TODO
+The \fIflags\fR argument is a bitmask with the following bits defined:
+.IP \fBDNS_NOSRCH\fR
+do not perform domain name search in search list.
+.IP \fBDNS_NORD\fR
+do not request recursion when performing queries
+(i.e. don't set RD flag in querues).
+.IP \fBDNS_AAONLY\fR
+request authoritative answers only (i.e. set AA
+flag in queries).
+.RE
+
+.PP
+.nf
+int \fBdns_open\fR(\fIctx\fR)
+int \fBdns_sock\fR(const \fIctx\fR)
+void \fBdns_close\fR(\fIctx\fR)
+.fi
+.RS
+\fBdns_open\fR() opens the UDP socket used for queries if not already
+open, and return assotiated filedescriptor (or negative value in case
+of error).  Before any query can be submitted, the context should be
+opened using this routine.  And before opening, the context should be
+initialized.
+\fBdns_sock\fR() return the UDP socket if open, or -1 if not.
+\fBdns_close\fR() closes the UDP socket if it was open, and drops all active
+queries if any.
+.RE
+
+.PP
+.nf
+int \fBdns_active\fR(const \fIctx\fR)
+.fi
+.RS
+return number of active queries queued for the given context
+\fIctx\fR, or zero if none.
+.RE
+
+.PP
+.nf
+int \fBdns_status\fR(const \fIctx\fR)
+.fi
+.RS
+return status code from last operation.  When using syncronous
+interface, this is the query completion status of the last query.
+With asyncronous interface, from within the callback routine,
+this is the query completion status of the query for which the
+callback is being called.  When query submission fails, this
+is the error code indicating failure reason.  All error codes
+are negative and are represented by \fBDNS_E_\fIXXX\fR constants
+described below.
+.RE
+
+.PP
+.nf
+void \fBdns_ioevent\fR(\fIctx\fR, time_t \fInow\fR)
+.fi
+.RS
+this routine may be called by an application to process I/O
+events on the UDP socket used by the library, as returned
+by \fBdns_sock\fR().  The routine tries to receive incoming
+UDP datagram from the socket and process it.  The socket is
+set up to be non-blocking, so it is safe to call the routine
+even if there's no data to read.  The routine will process
+as many datagrams as are queued for the socket, so it is
+safe to use it with either level-triggered or edge-triggered
+I/O monitoring model.  The \fInow\fR argument is either a
+current time as returned by \fBtime\fR(), or 0, in which
+case the routine will obtain current time by it's own.
+.RE
+
+.PP
+.nf
+int \fBdns_timeouts\fR(\fIctx\fR, int \fImaxwait\fR, time_t \fInow\fR)
+.fi
+.RS
+process any pending timeouts and return number of secounds
+from current time (\fInow\fR if it is not 0) to the time when
+the library wants the application to pass it control to process
+more queued requests.  In case when there are no requests pending,
+this time is -1.  The routine will not request a time larger than
+\fImaxwait\fR secounds if it is greather or equal to zero.  If
+\fInow\fR is 0, the routine will obtain current time by it's own;
+when it is not 0, it should contain current time as returned by
+\fBtime\fR().
+.RE
+
+.PP
+.nf
+typedef void \fBdns_utm_fn\fR(\fIctx\fR, int \fItimeout\fR, void *\fIdata\fR)
+void \fBdns_set_tmcbck\fR(\fIctx\fR, dns_utm_fn *\fIutmfn\fR, void *\fIdata\fR)
+.fi
+.RS
+An application may use custom callback-based I/O multiplexing mechanism.
+Usually such a mechanism have concept of a \fItimer\fR, and an ability
+to register a timer event in a form of a callback routine which will
+be executed after certain amount of time.  In order to use such an
+event mechanism, udns provides an ability to register and de-register
+timer events necessary for internal processing using whatever event
+mechanism an application uses.  For this to work, it is possible to
+assotiate a pointer to a routine that will perform necessary work for
+(de)registering timer events with a given resolver context, and
+udns will call that routine at appropriate times.  Prototype of
+such a routine is shown by \fBdns_utm_fn\fR typedef above.  Libudns
+assotiates single timer with resolver context.  User-supplied \fIutmfn\fR
+routine will be called by the library with the following arguments:
+.IP "\fIctx\fR == NULL"
+delete user timer, at context free time or when an application changes
+user timer request routine using \fBdns_set_tmcbck\fR();
+.IP "\fIctx\fR != NULL, \fItimeout\fR < 0"
+don't fire timer anymore, when there are no active requests;
+.IP "\fIctx\fR != NULL, \fItimeout\fR == 0"
+fire timer at the next possibility, but not immediately;
+.IP "\fIctx\fR != NULL, \fItimeout\fR > 0"
+fire timer after \fItimeout\fR seconds after now.
+.PP
+The \fIdata\fR argument passed to the routine will be the same
+as passed to \fBdns_set_tmcbck\fR().
+.PP
+When a timer expires, an application should call \fBdns_timeouts\fR()
+routine (see below).  Non-callback timer usage is provided too.
+.RE
+
+.PP
+.B XXXX TODO: some more resolver context routines, like dns_set_dbgfn() etc.
+
+.SH "QUERY INTERFACE"
+
+.PP
+There are two ways to perform DNS queries: traditional syncronous
+way, when udns performs all the necessary processing and return
+control to the application only when the query completes, and
+asyncronous way, when an application submits one or more queries
+to the library using given resolver context, and waits for completion
+by monitoring filedescriptor used by library and calling library
+routines to process input on that filedescriptor.  Asyncronous mode
+works with callback routines: an application supplies an address of
+a routine to execute when the query completes, and a data pointer,
+which is passed to the callback routine.
+
+.PP
+Queries are submitted to the library in a form of \fBstruct\ dns_query\fR.
+To perform asyncronous query, an application calls one of the
+\fBdns_submit_\fIXXX\fR() rounines, and provides necessary information
+for a callback, together with all the query parameters.
+When the query completes, library will call application-supplied callback
+routine, giving it the resolver context (wich holds query completion status),
+dynamically allocated result (which will be either raw DNS packet or, if
+applicatin requested parsing the result by specifying non-NULL parse routine,
+ready-to-use type-specific structure), and a data pointer provided by an
+application when it submitted the query.  It is the application who's
+responsible for freeing the result memory.
+.PP
+Generic query callback routine looks like this:
+.nf
+typedef void
+\fBdns_query_fn\fR(\fIctx\fR, void *\fIresult\fR, void *\fIdata\fR)
+.fi
+Type-specific query interface expects similar form of callback
+routine with the only difference in type of \fBresult\fR argument,
+which will be pointer to specific data structure (decoded reply)
+instead of this void pointer to raw DNS packet data.
+
+.PP
+Result parsing routine looks like this:
+.nf
+typedef int
+\fBdns_parse_fn\fR(const unsigned char *\fIqdn\fR,
+      const unsigned char *\fIpkt\fR,
+      const unsigned char *\fIcur\fR,
+      const unsigned char *\fIend\fR,
+      void **\fIresultp\fR);
+.fi
+When called by the library, the arguments are as follows:
+\fIpkt\fR points to the start of the packet received;
+\fIend\fR points past the end of the packet received;
+\fIcur\fR points past the query DN in the query section of the
+packet;
+\fIqdn\fR points to the original query DN.
+The routine should allocate a single buffer to hold the result,
+parse the reply filling in the buffer, and return the buffer
+using \fIresultp\fR argument.  It returns 0 in case of error,
+or udns error code (\fBDNS_E_\fIXXX\fR constants) in case of
+error.
+Note that by the time when the parse routine is called by the
+library, packet is already verified to be a reply to the
+original query, by matching query DN, query class and query type.
+
+.PP
+Type-specific query inteface supplies necessary parsing routines
+automatically.
+
+.PP
+In case of error, query completion status as returned by
+\fBdns_status\fR(\fIctx\fR), will contain one of the following values:
+.IP "positive value"
+length of raw DNS packet if parsing is not requested.
+.IP 0
+the query was successeful and the \fIreply\fR points to type-specific
+data structure.
+.IP \fBDNS_E_TEMPFAIL\fR
+temporary error, the resolver nameserver was not able to
+process our query or timed out.
+.IP \fBDNS_E_PROTOCOL\fR
+protocol error, a nameserver returned malformed reply.
+.IP \fBDNS_E_NXDOMAIN\fR
+the domain name does not exist.
+.IP \fBDNS_E_NODATA\fR
+there is no data of requested type found.
+.IP \fBDNS_E_NOMEM\fR
+out of memory while processing request.
+.IP \fBDNS_E_BADQUERY\fR
+some aspect of the query (most common is the domain name in question)
+is invalid, and the library can't even start a query.
+
+.PP
+Library provides two series of routines which uses similar interface --
+one for asyncronous queries and another for syncronous queries.  There
+are two general low-level routines in each series to submit (asyncronous
+interface) and resolve (syncronous interface) queries, as well as several
+type-specific routines with more easy-to-use interfaces.  To submit
+an asyncronous query, use one of \fBdns_submit_\fIXXX\fR() routine, each
+of which accepts query parameters, pointers to callback routine and to
+callback data, and optional current time hint.  Note type-specific
+\fBdns_submit_\fIXXX\fR() routines expects specific type of the callback
+routine as well, which accepts reply as a pointer to corresponding
+structure, not a void pointer).  Every \fBdns_submit_\fIXXX\fR() routine
+return pointer to internal query structure of type struct\ dns_query,
+used as an identifier for the given query.
+
+.PP
+To resolve a query syncronously, use one of \fBdns_resolve_\fIXXX\fR()
+routines, which accepts the same query parameters (but not the
+callback pointers) as corresponding \fBdns_submit_\fIXXX\fR(), and
+return the query result, which is the same as passed to the callback
+routine in case of asyncronous interface.
+
+.PP
+In either case, the result memory (if the query completed successefully)
+is dynamically allocated and should be freed by an application.  If
+the query failed for any reason, the result will be NULL, and error
+status will be available from \fBdns_status\fR(\fIctx\fR) routine
+as shown above.
+
+.PP
+.nf
+struct dns_query *
+\fBdns_submit_dn\fR(\fIctx\fR,
+     const unsigned char *\fIdn\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR,
+     \fIparse\fR, \fIcbck\fR, \fIdata\fR)
+struct dns_query *
+\fBdns_submit_p\fR(\fIctx\fR,
+     const char *\fIname\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR,
+     \fIparse\fR, \fIcbck\fR, \fIdata\fR)
+   enum dns_class \fIqcls\fR;
+   enum dns_type \fIqtyp\fR;
+   int \fIflags\fR;
+   dns_parse_fn *\fIparse\fR;
+   dns_query_fn *\fIcbck\fR;
+   void *\fIdata\fR;
+.fi
+.RS
+submit a query for processing for the given resolver context \fIctx\fR.
+Two routines differs only in 3rd argument, which is domain name in
+DN format (\fIdn\fR) or asciiz string (\fIname\fR).  The query will be
+performed for the given domain name, with type \fIqtyp\fR in class \fIqcls\fR,
+using option bits in \fIflags\fR, using RR parsing routine pointed by
+\fIparse\fR if not-NULL, and upon completion, \fIcbck\fR function will
+be called with the \fIdata\fR argument.
+In case of successeful query submission,
+the routine return pointer to internal query structure which may be treated
+as an identifier of the query as used by the library, and may be used as an
+argument for \fBdns_cancel\fR() routine.  In case of error, NULL will be
+returned, and context error status (available using \fIdns_status\fR() routine)
+will be set to corresponding error code, which in this case may be
+DNS_E_BADQUERY if the \fIname\fR of \fIdn\fR is invalid, DNS_E_NOMEM if
+there's no memory available to allocate query structure, or DNS_E_TEMPFAIL
+if an internal error occured.
+.RE
+
+.PP
+.nf
+void *\fBdns_resolve_dn\fR(\fIctx\fR,
+     const unsigned char *\fIdn\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, \fIparse\fR);
+void *\fBdns_resolve_p\fR(\fIctx\fR,
+     const char *\fIname\fR, \fIqcls\fR, \fIqtyp\fR, \fIflags\fR, \fIparse\fR)
+   enum dns_class \fIqcls\fR;
+   enum dns_type \fIqtyp\fR;
+   int \fIflags\fR;
+   dns_parse_fn *\fIparse\fR;  
+.fi
+.RS
+syncronous interface.  The routines perform all the steps necessary to resolve
+the given query and return the result.  If there's no positive result for any
+reason, all the routines return NULL, and set context error status (available 
+using \fBdns_status\fR() routine) to indicate the error code.  If the query
+was successeful, context status code will contain either the length of the
+raw DNS reply packet if \fIparse\fR argument was NULL (in which case the return
+value is pointer to the reply DNS packet), or 0 (in which case the return value
+is the result of \fIparse\fR routine).  If the query successeful (return value
+is not NULL), the memory returned was dynamically allocated by the library
+and should be free()d by application after use.
+.RE
+
+.PP
+.nf
+void *\fBdns_resolve\fR(\fIctx\fR, struct dns_query *\fIq\fR)
+.fi
+.RS
+wait for the given query \fIq\fR, as returned by one of
+\fBdns_submit_\fIXXX\fR() routines, for completion, and
+return the result.  The callback routine will not be called
+for this query.  After completion, the query identifier \fIq\fR
+is not valid. Both \fBdns_resolve_dn\fR() and \fBdns_resolve_p\fR()
+are just wrappers around corresponding submit routines and this
+\fBdns_resolve\fR() routine.
+.RE
+
+.PP
+.nf
+void \fBdns_cancel\fR(\fIctx\fR, struct dns_query *\fIq\fR)
+.fi
+.RS
+cancel an active query \fIq\fR, without calling a callback routine.
+After completion, the query identifier \fIq\fR is not valid.
+.RE
+
+.SH "TYPE-SPECIFIC QUERIES"
+
+.PP
+In addition to the generic low-level query interface, the library provides
+a set of routines to perform specific queries in a type-safe manner, as
+well as parsers for several well-known resource record types.  The library
+implements high-level interface for A, AAAA, PTR, MX and TXT records
+and DNSBL and RHSBL functionality.  These routines returns specific types
+as result of a query, instead of raw DNS packets.  The following types
+and routines are available.
+
+.PP
+.nf
+struct \fBdns_rr_null\fR {
+  char *\fBdnsn_qname\fR;     /* original query name */
+  char *\fBdnsn_cname\fR;     /* canonical name */
+  unsigned \fBdnsn_ttl\fR;    /* Time-To-Live (TTL) value */
+  int \fBdnsn_nrr\fR;         /* number of records in the set */
+};
+.fi
+.PP
+NULL RR set, used as a base for all other RR type structures.
+Every RR structure as used by the library have four standard
+fields as in struct\ \fBdns_rr_null\fR.
+
+.SS "IN A Queries"
+.PP
+.nf
+struct \fBdns_rr_a4\fR {       /* IN A RRset */
+  char *\fBdnsa4_qname\fR;     /* original query name */
+  char *\fBdnsa4_cname\fR;     /* canonical name */
+  unsigned \fBdnsa4_ttl\fR;    /* Time-To-Live (TTL) value */
+  int \fBdnsa4_nrr\fR;         /* number of addresses in the set */
+  struct in_addr \fBdnsa4_addr\fR[]; /* array of addresses */
+};
+typedef void
+  \fBdns_query_a4_fn\fR(\fIctx\fR, struct dns_rr_a4 *\fIresult\fR, \fIdata\fR)
+dns_parse_fn \fBdns_parse_a4\fB;
+struct dns_query *
+\fBdns_submit_a4\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR,
+   dns_query_a4_fn *\fIcbck\fR, \fIdata\fR);
+struct dns_rr_a4 *
+\fBdns_resolve_a4\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR);
+.fi
+.PP
+The \fBdns_rr_a4\fR structure holds a result of an \fBIN A\fR query,
+which is an array of IPv4 addresses.  Callback routine for IN A queries
+expected to be of type \fBdns_query_a4_fn\fR, which expects pointer to
+\fBdns_rr_a4\fR structure as query result instead of raw DNS packet.
+The \fBdns_parse_a4\fR() is used to convert raw DNS reply packet into
+\fBdns_rr_a4\fR structure (it is used internally and may be used directly too
+with generic query interface).  Routines \fBdns_submit_a4\fR() and
+\fBdns_resolve_a4\fR() are used to perform A IN queries in a type-safe
+manner.  The \fIname\fR parameter is the domain name in question, and
+\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical
+interest (if the \fIname\fR is absolute, that is, it ends up with a dot,
+DNS_NOSRCH flag will be set automatically).
+
+.SS "IN AAAA Queries"
+.PP
+.nf
+struct \fBdns_rr_a6\fR {       /* IN AAAA RRset */
+  char *\fBdnsa6_qname\fR;     /* original query name */
+  char *\fBdnsa6_cname\fR;     /* canonical name */
+  unsigned \fBdnsa6_ttl\fR;    /* Time-To-Live (TTL) value */
+  int \fBdnsa6_nrr\fR;         /* number of addresses in the set */
+  struct in6_addr \fBdnsa6_addr\fR[]; /* array of addresses */
+};
+typedef void
+  \fBdns_query_a6_fn\fR(\fIctx\fR, struct dns_rr_a6 *\fIresult\fR, \fIdata\fR)
+dns_parse_fn \fBdns_parse_a6\fB;
+struct dns_query *
+\fBdns_submit_a6\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR,
+   dns_query_a6_fn *\fIcbck\fR, \fIdata\fR);
+struct dns_rr_a6 *
+\fBdns_resolve_a6\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR);
+.fi
+.PP
+The \fBdns_rr_a6\fR structure holds a result of an \fBIN AAAA\fR query,
+which is an array of IPv6 addresses.  Callback routine for IN AAAA queries
+expected to be of type \fBdns_query_a6_fn\fR, which expects pointer to
+\fBdns_rr_a6\fR structure as query result instead of raw DNS packet.
+The \fBdns_parse_a6\fR() is used to convert raw DNS reply packet into
+\fBdns_rr_a6\fR structure (it is used internally and may be used directly too
+with generic query interface).  Routines \fBdns_submit_a6\fR() and
+\fBdns_resolve_a6\fR() are used to perform AAAA IN queries in a type-safe
+manner.  The \fIname\fR parameter is the domain name in question, and
+\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical
+interest (if the \fIname\fR is absolute, that is, it ends up with a dot,
+DNS_NOSRCH flag will be set automatically).
+
+.SS "IN PTR Queries"
+.PP
+.nf
+struct \fBdns_rr_ptr\fR {       /* IN PTR RRset */
+  char *\fBdnsptr_qname\fR;     /* original query name */
+  char *\fBdnsptr_cname\fR;     /* canonical name */
+  unsigned \fBdnsptr_ttl\fR;    /* Time-To-Live (TTL) value */
+  int \fBdnsptr_nrr\fR;         /* number of domain name pointers */
+  char *\fBdnsptr_ptr\fR[];     /* array of domain name pointers */
+};
+typedef void
+  \fBdns_query_ptr_fn\fR(\fIctx\fR, struct dns_rr_ptr *\fIresult\fR, \fIdata\fR)
+dns_parse_fn \fBdns_parse_ptr\fB;
+struct dns_query *
+\fBdns_submit_a4ptr\fB(\fIctx\fR, const struct in_addr *\fBaddr\fR,
+   dns_query_ptr_fn *\fIcbck\fR, \fIdata\fR);
+struct dns_rr_ptr *
+\fBdns_resolve_a4ptr\fB(\fIctx\fR, const struct in_addr *\fBaddr\fR);
+struct dns_query *
+\fBdns_submit_a6ptr\fB(\fIctx\fR, const struct in6_addr *\fBaddr\fR,
+   dns_query_ptr_fn *\fIcbck\fR, \fIdata\fR);
+struct dns_rr_ptr *
+\fBdns_resolve_a6ptr\fB(\fIctx\fR, const struct in6_addr *\fBaddr\fR);
+.fi
+.PP
+The \fBdns_rr_ptr\fR structure holds a result of an IN PTR query, which
+is an array of domain name pointers for a given IPv4 or IPv6 address.
+Callback routine for IN PTR queries expected to be of type
+\fBdns_query_ptr_fn\fR, which expects pointer to \fBdns_rr_ptr\fR
+structure as query result instead of raw DNS packet.  The \fBdns_parse_ptr\fR()
+is used to convert raw DNS reply packet into \fBdns_rr_ptr\fR structure
+(it is used internally and may be used directly too with generic query
+interface).  Routines \fBdns_submit_a4ptr\fR() and \fBdns_resolve_a4ptr\fR()
+are used to perform IN PTR queries for IPv4 addresses in a type-safe
+manner. Routines \fBdns_submit_a6ptr\fR() and \fBdns_resolve_a6ptr\fR()
+are used to perform IN PTR queries for IPv6 addresses.
+
+.SS "IN MX Queries"
+.PP
+.nf
+struct \fBdns_mx\fR {          /* single MX record */
+  int \fBpriority\fR;          /* priority value of this MX */
+  char *\fBname\fR;            /* domain name of this MX */
+};
+struct \fBdns_rr_mx\fR {       /* IN MX RRset */
+  char *\fBdnsmx_qname\fR;     /* original query name */
+  char *\fBdnsmx_cname\fR;     /* canonical name */
+  unsigned \fBdnsmx_ttl\fR;    /* Time-To-Live (TTL) value */
+  int \fBdnsmx_nrr\fR;         /* number of mail exchangers in the set */
+  struct dns_mx \fBdnsmx_mx\fR[]; /* array of mail exchangers */
+};
+typedef void
+  \fBdns_query_mx_fn\fR(\fIctx\fR, struct dns_rr_mx *\fIresult\fR, \fIdata\fR)
+dns_parse_fn \fBdns_parse_mx\fB;
+struct dns_query *
+\fBdns_submit_mx\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR,
+   dns_query_mx_fn *\fIcbck\fR, \fIdata\fR);
+struct dns_rr_mx *
+\fBdns_resolve_mx\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR);
+.fi
+.PP
+The \fBdns_rr_mx\fR structure holds a result of an IN MX query, which
+is an array of mail exchangers for a given domain.  Callback routine for IN MX
+queries expected to be of type \fBdns_query_mx_fn\fR, which expects pointer to
+\fBdns_rr_mx\fR structure as query result instead of raw DNS packet.
+The \fBdns_parse_mx\fR() is used to convert raw DNS reply packet into
+\fBdns_rr_mx\fR structure (it is used internally and may be used directly too
+with generic query interface).  Routines \fBdns_submit_mx\fR() and
+\fBdns_resolve_mx\fR() are used to perform IN MX queries in a type-safe
+manner.  The \fIname\fR parameter is the domain name in question, and
+\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical
+interest (if the \fIname\fR is absolute, that is, it ends up with a dot,
+DNS_NOSRCH flag will be set automatically).
+
+.SS "TXT Queries"
+.PP
+.nf
+struct \fBdns_txt\fR {          /* single TXT record */
+  int \fBlen\fR;                /* length of the text */
+  unsigned char *\fBtxt\fR;     /* pointer to the text */
+};
+struct \fBdns_rr_txt\fR {       /* TXT RRset */
+  char *\fBdnstxt_qname\fR;     /* original query name */
+  char *\fBdnstxt_cname\fR;     /* canonical name */
+  unsigned \fBdnstxt_ttl\fR;    /* Time-To-Live (TTL) value */
+  int \fBdnstxt_nrr\fR;         /* number of text records in the set */
+  struct dns_txt \fBdnstxt_txt\fR[]; /* array of TXT records */
+};
+typedef void
+  \fBdns_query_txt_fn\fR(\fIctx\fR, struct dns_rr_txt *\fIresult\fR, \fIdata\fR)
+dns_parse_fn \fBdns_parse_txt\fB;
+struct dns_query *
+\fBdns_submit_txt\fB(\fIctx\fR, const char *\fIname\fR, enum dns_class \fIqcls\fR,
+   int \fIflags\fR, dns_query_txt_fn *\fIcbck\fR, \fIdata\fR);
+struct dns_rr_txt *
+\fBdns_resolve_txt\fB(\fIctx\fR, const char *\fIname\fR,
+             enum dns_class \fIqcls\fR, int \fIflags\fR);
+.fi
+.PP
+The \fBdns_rr_txt\fR structure holds a result of a TXT query, which is an
+array of text records for a given domain name.  Callback routine for TXT
+queries expected to be of type \fBdns_query_txt_fn\fR, which expects pointer
+to \fBdns_rr_txt\fR structure as query result instead of raw DNS packet.
+The \fBdns_parse_txt\fR() is used to convert raw DNS reply packet into
+\fBdns_rr_txt\fR structure (it is used internally and may be used directly too
+with generic query interface).  Routines \fBdns_submit_txt\fR() and
+\fBdns_resolve_txt\fR() are used to perform IN MX queries in a type-safe
+manner.  The \fIname\fR parameter is the domain name in question, and
+\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical
+interest (if the \fIname\fR is absolute, that is, it ends up with a dot,
+DNS_NOSRCH flag will be set automatically).  Note that each TXT string
+is represented by \fBstruct\ dns_txt\fR, while zero-terminated (and the
+len field of the structure does not include the terminator), may contain
+embedded null characters -- content of TXT records is not interpreted
+by the library in any way.
+
+.SS "SRV Queries"
+.PP
+.nf
+struct \fBdns_srv\fR {          /* single SRV record */
+  int \fBpriority\fR;           /* priority of the record */
+  int \fBweight\fR;             /* weight of the record */
+  int \fBport\fR;               /* the port number to connect to */
+  char *\fBname\fR;             /* target host name */
+};
+struct \fBdns_rr_srv\fR {       /* SRV RRset */
+  char *\fBdnssrv_qname\fR;     /* original query name */
+  char *\fBdnssrv_cname\fR;     /* canonical name */
+  unsigned \fBdnssrv_ttl\fR;    /* Time-To-Live (TTL) value */
+  int \fBdnssrv_nrr\fR;         /* number of text records in the set */
+  struct dns_srv \fBdnssrv_srv\fR[]; /* array of SRV records */
+};
+typedef void
+  \fBdns_query_srv_fn\fR(\fIctx\fR, struct dns_rr_srv *\fIresult\fR, \fIdata\fR)
+dns_parse_fn \fBdns_parse_srv\fB;
+struct dns_query *
+\fBdns_submit_srv\fB(\fIctx\fR, const char *\fIname\fR, const char *\fIservice\fR, const char *\fIprotocol\fR,
+   int \fIflags\fR, dns_query_txt_fn *\fIcbck\fR, \fIdata\fR);
+struct dns_rr_srv *
+\fBdns_resolve_srv\fB(\fIctx\fR, const char *\fIname\fR, const char *\fIservice\fR, const char *\fIprotocol\fR,
+             int \fIflags\fR);
+.fi
+.PP
+The \fBdns_rr_srv\fR structure holds a result of an IN SRV (rfc2782) query,
+which is an array of servers (together with port numbers) which are performing
+operations for a given \fIservice\fR using given \fIprotocol\fR on a target
+domain \fIname\fR.  Callback routine for IN SRV queries expected to be of type
+\fBdns_query_srv_fn\fR, which expects pointer to \fBdns_rr_srv\fR structure as
+query result instead of raw DNS packet.  The \fBdns_parse_srv\fR() is used to
+convert raw DNS reply packet into \fBdns_rr_srv\fR structure (it is used
+internally and may be used directly too with generic query interface).
+Routines \fBdns_submit_srv\fR() and \fBdns_resolve_srv\fR() are used to
+perform IN SRV queries in a type-safe manner.  The \fIname\fR parameter
+is the domain name in question, \fIservice\fR and \fRprotocl\fR specifies the
+service and the protocol in question (the library will construct query DN
+according to rfc2782 rules) and may be NULL (in this case the library
+assumes \fIname\fR parameter holds the complete SRV query), and
+\fIflags\fR is query flags bitmask, with one bit, DNS_NOSRCH, of practical
+interest (if the \fIname\fR is absolute, that is, it ends up with a dot,
+DNS_NOSRCH flag will be set automatically).
+
+.SS "NAPTR Queries"
+.PP
+.nf
+struct \fBdns_naptr\fR {        /* single NAPTR record */
+  int \fBorder\fR;              /* record order */
+  int \fBpreference\fR;         /* preference of this record */
+  char *\fBflags\fR;            /* application-specific flags */
+  char *\fBservice\fR;          /* service parameter */
+  char *\fBregexp\fR;           /* substitutional regular expression */
+  char *\fBreplacement\fR;      /* replacement string */
+};
+struct \fBdns_rr_naptr\fR {     /* NAPTR RRset */
+  char *\fBdnsnaptr_qname\fR;   /* original query name */
+  char *\fBdnsnaptr_cname\fR;   /* canonical name */
+  unsigned \fBdnsnaptr_ttl\fR;  /* Time-To-Live (TTL) value */
+  int \fBdnsnaptr_nrr\fR;       /* number of text records in the set */
+  struct dns_naptr \fBdnsnaptr_naptr\fR[]; /* array of NAPTR records */
+};
+typedef void
+  \fBdns_query_naptr_fn\fR(\fIctx\fR, struct dns_rr_naptr *\fIresult\fR, \fIdata\fR)
+dns_parse_fn \fBdns_parse_naptr\fB;
+struct dns_query *
+\fBdns_submit_naptr\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR,
+   dns_query_txt_fn *\fIcbck\fR, \fIdata\fR);
+struct dns_rr_naptr *
+\fBdns_resolve_naptr\fB(\fIctx\fR, const char *\fIname\fR, int \fIflags\fR);
+.fi
+.PP
+The \fBdns_rr_naptr\fR structure holds a result of an IN NAPTR (rfc3403) query.
+Callback routine for IN NAPTR queries expected to be of type
+\fBdns_query_naptr_fn\fR, expects pointer to \fBdns_rr_naptr\fR
+structure as query result instead of raw DNS packet.
+The \fBdns_parse_naptr\fR() is used to convert raw DNS reply packet into
+\fBdns_rr_naptr\fR structure (it is used
+internally and may be used directly too with generic query interface).
+Routines \fBdns_submit_naptr\fR() and \fBdns_resolve_naptr\fR() are used to
+perform IN NAPTR queries in a type-safe manner.  The \fIname\fR parameter
+is the domain name in question, and \fIflags\fR is query flags bitmask,
+with one bit, DNS_NOSRCH, of practical interest (if the \fIname\fR is
+absolute, that is, it ends up with a dot, DNS_NOSRCH flag will be set
+automatically).
+
+.SS "DNSBL Interface"
+.PP
+A DNS-based blocklists, or a DNSBLs, are in wide use nowadays, especially
+to protect mailservers from spammers.  The library provides DNSBL interface,
+a set of routines to perform queries against DNSBLs.  Routines accepts an
+IP address (IPv4 and IPv6 are both supported) and a base DNSBL zone as
+query parameters, and returns either \fBdns_rr_a4\fR or \fBdns_rr_txt\fR
+structure.  Note that IPv6 interface return IPv4 RRset.
+.PP
+.nf
+struct dns_query *
+\fBdns_submit_a4dnsbl\fR(\fIctx\fR,
+  const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR,
+  dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR);
+struct dns_query *
+\fBdns_submit_a4dnsbl_txt\fR(\fIctx\fR,
+  const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR,
+  dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR);
+struct dns_query *
+\fBdns_submit_a6dnsbl\fR(\fIctx\fR,
+  const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR,
+  dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR);
+struct dns_query *
+\fBdns_submit_a6dnsbl_txt\fR(\fIctx\fR,
+  const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR,
+  dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR);
+struct dns_rr_a4 *\fBdns_resolve_a4dnsbl\fR(\fIctx\fR,
+  const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR)
+struct dns_rr_txt *\fBdns_resolve_a4dnsbl_txt\fR(\fIctx\fR,
+  const struct in_addr *\fIaddr\fR, const char *\fIdnsbl\fR)
+struct dns_rr_a4 *\fBdns_resolve_a6dnsbl\fR(\fIctx\fR,
+  const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR)
+struct dns_rr_txt *\fBdns_resolve_a6dnsbl_txt\fR(\fIctx\fR,
+  const struct in6_addr *\fIaddr\fR, const char *\fIdnsbl\fR)
+.fi
+Perform (submit or resolve) a DNSBL query for the given \fIdnsbl\fR
+domain and an IP \fIaddr\fR in question, requesting either A or TXT
+records.
+
+.SS "RHSBL Interface"
+.PP
+RHSBL is similar to DNSBL, but instead of an IP address, the
+parameter is a domain name.
+.PP
+.nf
+struct dns_query *
+\fBdns_submit_rhsbl\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR,
+  dns_query_a4_fn *\fIcbck\fR, void *\fIdata\fR);
+struct dns_query *
+\fBdns_submit_rhsbl_txt\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR,
+  dns_query_txt_fn *\fIcbck\fR, void *\fIdata\fR);
+struct dns_rr_a4 *
+\fBdns_resolve_rhsbl\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR);
+struct dns_rr_txt *
+\fBdns_resolve_rhsbl_txt\fR(\fIctx\fR, const char *\fIname\fR, const char *\fIrhsbl\fR);
+.fi
+Perform (submit or resolve) a RHSBL query for the given \fIrhsbl\fR
+domain and \fIname\fR in question, requesting either A or TXT records.
+
+
+.SH "LOW-LEVEL INTERFACE"
+
+.SS "Domain Names (DNs)"
+
+.PP
+A DN is a series of domain name labels each starts with length byte,
+followed by empty label (label with zero length).  The following
+routines to work with DNs are provided.
+
+.PP
+.nf
+unsigned \fBdns_dnlen\fR(const unsigned char *\fIdn\fR)
+.fi
+.RS
+return length of the domain name \fIdn\fR, including the terminating label.
+.RE
+
+.PP
+.nf
+unsigned \fBdns_dnlabels\fR(const unsigned char *\fIdn\fR)
+.fi
+.RS
+return number of non-zero labels in domain name \fIdn\fR.
+.RE
+
+.PP
+.nf
+unsigned \fBdns_dnequal\fR(\fIdn1\fR, \fIdn2\fR)
+  const unsigned char *\fIdn1\fR, *\fIdn2\fR;
+.fi
+.RS
+test whenever the two domain names, \fIdn1\fR and \fIdn2\fR, are
+equal (case-insensitive).  Return domain name length if equal
+or 0 if not.
+.RE
+
+.PP
+.nf
+unsigned \fBdns_dntodn\fR(\fIsdn\fR, \fIddn\fR, \fIdnsiz\fR)
+  const unsigned char *\fIsdn\fR;
+  unsigned char *\fIddn\fR;
+  unsigned \fIdnsiz\fR;
+.fi
+.RS
+copies the source domain name \fIsdn\fR to destination buffer \fIddn\fR
+of size \fIdnsiz\fR.  Return domain name length or 0 if \fIddn\fR is
+too small.
+.RE
+
+.PP
+.nf
+int \fBdns_ptodn\fR(\fIname\fR, \fInamelen\fR, \fIdn\fR, \fIdnsiz\fR, \fIisabs\fR)
+int \fBdns_sptodn\fR(\fIname\fR, \fIdn\fR, \fIdnsiz\fR)
+  const char *\fIname\fR; unsigned \fInamelen\fR;
+  unsigned char *\fIdn\fR; unsigned \fIdnsiz\fR;
+  int *\fIisabs\fR;
+.fi
+.RS
+convert asciiz name \fIname\fR of length \fInamelen\fR to DN format,
+placing result into buffer \fIdn\fR of size \fIdnsiz\fR.  Return
+length of the DN if successeful, 0 if the \fIdn\fR buffer supplied is
+too small, or negative value if \fIname\fR is invalid.  If \fIisabs\fR
+is non-NULL and conversion was successeful, *\fIisabs\fR will be set to
+either 1 or 0 depending whenever \fIname\fR was absolute (i.e. ending with
+a dot) or not.  Name length, \fInamelength\fR, may be zero, in which case
+strlen(\fIname\fR) will be used.  Second form, \fBdns_sptodn\fR(), is a
+simplified form of \fBdns_ptodn\fR(), equivalent to
+.br
+.nf
+\fBdns_ptodn\fR(\fIname\fR, 0, \fIdn\fR, \fIdnlen\fR, 0).
+.fi
+.RE
+
+.PP
+.nf
+extern const unsigned char \fBdns_inaddr_arpa_dn\fR[]
+int \fBdns_a4todn\fR(const struct in_addr *\fIaddr\fR, const unsigned char *\fItdn\fR,
+      unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR)
+int \fBdns_a4ptodn\fR(const struct in_addr *\fIaddr\fR, const char *\fItname\fR,
+      unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR)
+extern const unsigned char \fBdns_ip6_arpa_dn\fR[]
+int \fBdns_a6todn\fR(const struct in6_addr *\fIaddr\fR, const unsigned char *\fItdn\fR,
+      unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR)
+int \fBdns_a6ptodn\fR(const struct in6_addr *\fIaddr\fR, const char *\fItname\fR,
+      unsigned char *\fIdn\fR, unsigned \fIdnsiz\fR)
+.fi
+.RS
+several variants of routines to convert IPv4 and IPv6 address \fIaddr\fR
+into reverseDNS-like domain name in DN format, storing result in \fIdn\fR
+of size \fIdnsiz\fR.  \fItdn\fR (or \fItname\fR) is the base zone name,
+like in-addr.arpa for IPv4 or in6.arpa for IPv6.  If \fItdn\fR (or \fItname\fR)
+is NULL, \fBdns_inaddr_arpa_dn\fR (or \fBdns_ip6_arpa_dn\fR) will be used.
+The routines may be used to construct a DN for a DNSBL lookup for example.
+All routines return length of the resulting DN on success, -1 if resulting
+DN is invalid, or 0 if the \fIdn\fR buffer (\fIdnsiz\fR) is too small.
+To hold standard rDNS DN, a buffer of size \fBDNS_A4RSIZE\fR (30 bytes) for
+IPv4 address, or \fBDNS_A6RSIZE\fR (74 bytes) for IPv6 address, is sufficient.
+.RE
+
+.PP
+.nf
+int \fBdns_dntop\fR(\fIdn\fR, \fIname\fR, \fInamesiz\fR)
+   const unsigned char *\fIdn\fR;
+   const char *\fIname\fR; unsigned \fInamesiz\fR;
+.fi
+.RS
+convert domain name \fIdn\fR in DN format to asciiz string, placing result
+into \fIname\fR buffer of size \fInamesiz\fR.  Maximum length of asciiz
+representation of domain name is \fBDNS_MAXNAME\fR (1024) bytes.  Root
+domain is represented as empty string.  Return length of the resulting name
+(including terminating character, i.e. strlen(name)+1) on success, 0 if the
+\fIname\fR buffer is too small, or negative value if \fIdn\fR is invalid
+(last case should never happen since all routines in this library which
+produce domain names ensure the DNs generated are valid).
+.RE
+
+.PP
+.nf
+const char *\fBdns_dntosp\fR(const unsigned char *\fIdn\fR)
+.fi
+.RS
+convert domain name \fIdn\fR in DN format to asciiz string using static
+buffer.  Return the resulting asciiz string on success or NULL on failure.
+Note since this routine uses static buffer, it is not thread-safe.
+.RE
+
+.PP
+.nf
+unsigned \fBdns_dntop_size\fR(const unsigned char *\fIdn\fR)
+.fi
+.RS
+return the buffer size needed to convert the \fIdn\fR domain name
+in DN format to asciiz string, for \fBdns_dntop\fR().  The routine
+return either the size of buffer required, including the trailing
+zero byte, or 0 if \fIdn\fR is invalid.
+.RE
+
+.SS "Working with DNS Packets"
+
+.PP
+The following routines are provided to encode and decode DNS on-wire
+packets.  This is low-level interface.
+
+.PP
+DNS response codes (returned by \fBdns_rcode\fR() routine) are
+defined as constants prefixed with \fBDNS_R_\fR.  See udns.h
+header file for the complete list.  In particular, constants
+\fBDNS_R_NOERROR\fR (0), \fBDNS_R_SERVFAIL\fR, \fBDNS_R_NXDOMAIN\fR
+may be of interest to an application.
+
+.PP
+.nf
+unsigned \fBdns_get16\fR(const unsigned char *\fIp\fR)
+unsigned \fBdns_get32\fR(const unsigned char *\fIp\fR)
+.fi
+.RS
+helper routines, convert 16-bit or 32-bit integer in on-wire
+format pointed to by \fIp\fR to unsigned.
+.RE
+
+.PP
+.nf
+unsigned char *\fBdns_put16\fR(unsigned char *\fId\fR, unsigned \fIn\fR)
+unsigned char *\fBdns_put32\fR(unsigned char *\fId\fR, unsigned \fIn\fR)
+.fi
+.RS
+helper routine, convert unsigned 16-bit or 32-bit integer \fIn\fR to
+on-wire format to buffer pointed to by \fId\fR, return \fId\fR+2 or
+\fId\fR+4.
+.RE
+
+.PP
+.nf
+\fBDNS_HSIZE\fR (12)
+.fi
+.RS
+defines size of DNS header.  Data section
+in the DNS packet immediately follows the header.  In the header,
+there are query identifier (id), various flags and codes,
+and number of resource records in various data sections.
+See udns.h header file for complete list of DNS header definitions.
+.RE
+
+.PP
+.nf
+unsigned \fBdns_qid\fR(const unsigned char *\fIpkt\fR)
+int \fBdns_rd\fR(const unsigned char *\fIpkt\fR)
+int \fBdns_tc\fR(const unsigned char *\fIpkt\fR)
+int \fBdns_aa\fR(const unsigned char *\fIpkt\fR)
+int \fBdns_qr\fR(const unsigned char *\fIpkt\fR)
+int \fBdns_ra\fR(const unsigned char *\fIpkt\fR)
+unsigned \fBdns_opcode\fR(const unsigned char *\fIpkt\fR)
+unsigned \fBdns_rcode\fR(const unsigned char *\fIpkt\fR)
+unsigned \fBdns_numqd\fR(const unsigned char *\fIpkt\fR)
+unsigned \fBdns_numan\fR(const unsigned char *\fIpkt\fR)
+unsigned \fBdns_numns\fR(const unsigned char *\fIpkt\fR)
+unsigned \fBdns_numar\fR(const unsigned char *\fIpkt\fR)
+const unsigned char *\fBdns_payload\fR(const unsigned char *\fIpkt\fR)
+.fi
+.RS
+return various parts from the DNS packet header \fIpkt\fR:
+query identifier (qid),
+recursion desired (rd) flag,
+truncation occured (tc) flag,
+authoritative answer (aa) flag,
+query response (qr) flag,
+recursion available (ra) flag,
+operation code (opcode),
+result code (rcode),
+number of entries in question section (numqd),
+number of answers (numan),
+number of authority records (numns),
+number of additional records (numar),
+and the pointer to the packet data (payload).
+.RE
+
+.PP
+.nf
+int \fBdns_getdn\fR(\fIpkt\fR, \fIcurp\fR, \fIpkte\fR, \fIdn\fR, \fIdnsiz\fR)
+const unsigned char *\fBdns_skipdn\fR(\fIcur\fR, \fIpkte\fR)
+   const unsigned char *\fIpkt\fR, *\fIpkte\fR, **\fIcurp\fR, *\fIcur\fR;
+   unsigned char *\fIdn\fR; unsigned \fIdnsiz\fR;
+.fi
+.RS
+\fBdns_getdn\fR() extract DN from DNS packet \fIpkt\fR which ends before
+\fIpkte\fR starting at position *\fIcurp\fR into buffer pointed to by
+\fIdn\fR of size \fIdnsiz\fR.  Upon successeful completion, *\fIcurp\fR
+will point to the next byte in the packet after the extracted domain name.
+It return positive number (length of the DN if \fIdn\fR) upon successeful
+completion, negative value on error (when the packet contains invalid data),
+or zero if the \fIdnsiz\fR is too small (maximum length of a domain name is
+\fBDNS_MAXDN\fR).  \fBdns_skipdn\fR() return pointer to the next byte in
+DNS packet which ends up before \fIpkte\fR after a domain name which starts
+at the \fIcur\fP byte, or NULL if the packet is invalid.  \fBdns_skipdn\fR()
+is more or less equivalent to what \fBdns_getdn\fR() does, except it does not
+actually extract the domain name in question, and uses simpler interface.
+.RE
+
+.PP
+.nf
+struct \fBdns_rr\fR {
+  unsigned char \fBdnsrr_dn\fR[DNS_MAXDN]; /* the RR DN name */
+  enum dns_class \fBdnsrr_cls\fR;          /* class of the RR */
+  enum dns_type  \fBdnsrr_typ\fR;          /* type of the RR */
+  unsigned \fBdnsrr_ttl\fR;                /* TTL value */
+  unsigned \fBdnsrr_dsz\fR;                /* size of data in bytes */
+  const unsigned char *\fBdnsrr_dptr\fR;   /* pointer to the first data byte */
+  const unsigned char *\fBdnsrr_dend\fR;   /* next byte after RR */
+};
+.fi
+.RS
+The \fBdns_rr\fR structure is used to hold information about
+single DNS Resource Record (RR) in an easy to use form.
+.RE
+
+.PP
+.nf
+struct \fBdns_parse\fR {
+  const unsigned char *\fBdnsp_pkt\fR; /* pointer to the packet being parsed */
+  const unsigned char *\fBdnsp_end\fR; /* end of the packet pointer */
+  const unsigned char *\fBdnsp_cur\fR; /* current packet positionn */
+  const unsigned char *\fBdnsp_ans\fR; /* pointer to the answer section */
+  int \fBdnsp_rrl\fR;                  /* number of RRs left */
+  int \fBdnsp_nrr\fR;                  /* number of relevant RRs seen so far */
+  unsigned \fBdnsp_ttl\fR;             /* TTL value so far */
+  const unsigned char *\fBdnsp_qdn\fR; /* the domain of interest or NULL */
+  enum dns_class \fBdnsp_qcls\fR;      /* class of interest or 0 for any */
+  enum dns_type  \fBdnsp_qtyp\fR;      /* type of interest or 0 for any */
+  unsigned char \fBdnsp_dnbuf\fR[DNS_MAXDN]; /* domain name buffer */
+};
+.fi
+.RS
+The \fBdns_parse\fR structure is used to parse DNS reply packet.
+It holds information about the packet being parsed (dnsp_pkt, dnsp_end and
+dnsp_cur fields), number of RRs in the current section left to do, and
+the information about specific RR which we're looking for (dnsp_qdn,
+dnsp_qcls and dnsp_qtyp fields).
+.RE
+
+.PP
+.nf
+int \fBdns_initparse\fR(struct dns_parse *\fIp\fR,
+  const unsigned char *\fIqdn\fR,
+  const unsigned char *\fIpkt\fR,
+  const unsigned char *\fIcur\fR,
+  const unsigned char *\fIend\fR)
+.fi
+.RS
+initializes the RR parsing structure \fIp\fR.  Arguments \fIpkt\fR, \fIcur\fR
+and \fIend\fR should describe the received packet: \fIpkt\fR is the start of
+the packet, \fIend\fR points to the next byte after the end of the packet,
+and \fIcur\fR points past the query DN in query section (to query class+type
+information).  And \fIqdn\fR points to the query DN.  This is the arguments
+passed to \fBdns_parse_fn\fR() routine. \fBdns_initparse\fR() initializes
+\fBdnsp_pkt\fR, \fBdnsp_end\fR and \fBdnsp_qdn\fR fields to the corresponding
+arguments, extracts and initializes \fBdnsp_qcls\fR and \fBdnsp_qtyp\fR
+fields to the values found at \fIcur\fR pointer, initializes
+\fBdnsp_cur\fR and \fBdnsp_ans\fR fields to be \fIcur\fR+4 (to the start of
+answer section), and initializes \fBdnsp_rrl\fR field to be number of entries
+in answer section. \fBdnsp_ttl\fR will be set to max TTL value, 0xffffffff,
+and \fBdnsp_nrr\fR to 0.
+.RE
+
+.PP
+.nf
+int \fBdns_nextrr\fR(struct dns_parse *\fIp\fR, struct dns_rr *\fIrr\fR);
+.fi
+.RS
+searches for next RR in the packet based on the criteria provided in
+the \fIp\fR structure, filling in the \fIrr\fR structure and
+advancing \fIp\fR->\fBdnsp_cur\fR to the next RR in the packet.
+RR selection is based on dnsp_qdn, dnsp_qcls and dnsp_qtyp fields in
+the dns_parse structure.  Any (or all) of the 3 fields may be 0,
+which means any actual value from the packet is acceptable.  In case
+the field isn't 0 (or NULL for dnsp_qdn), only RRs with corresponding
+characteristics are acceptable.  Additionally, when dnsp_qdn is non-NULL,
+\fBdns_nextrr\fR() performs automatic CNAME expansion.
+Routine will return positive value on success, 0 in case it reached the end
+of current section in the packet (\fIp\fR->\fBdnsp_rrl\fR is zero), or
+negative value if next RR can not be decoded (packet format is invalid).
+The routine updates \fIp\fR->\fBdnsp_qdn\fR automatically when this
+field is non-NULL and it encounters appropriate CNAME RRs (saving CNAME
+target in \fIp\fR->\fBdnsp_dnbuf\fR), so after end of the process,
+\fIp\fR->\fBdnsp_qdn\fR will point to canonical name of the domain
+in question.  The routine updates \fIp\fR->\fBdnsp_ttl\fR value to
+be the minimum TTL of all RRs found.
+.RE
+
+.PP
+.nf
+void \fBdns_rewind\fR(struct dns_parse *\fIp\fR, const unsigned char *\fIqdn\fR)
+.fi
+.RS
+this routine "rewinds" the packet parse state structure to be at the
+same state as after a call to \fBdns_initparse\fR(), i.e. reposition
+the parse structure \fIp\fR to the start of answer section and
+initialize \fIp\fR->\fBdnsp_rrl\fR to the number of entries in
+answer section.
+.RE
+
+.PP
+.nf
+int \fBdns_stdrr_size\fR(const struct dns_parse *\fIp\fR);
+.fi
+.RS
+return size to hold standard RRset structure information, as shown
+in \fBdns_rr_null\fR structure (for the query and canonical
+names).  Used to calculate amount of memory to allocate for common
+part of type-specific RR structures in parsing routines.
+.RE
+
+.PP
+.nf
+void *\fBdns_stdrr_finish\fR(struct dns_rr_null *\fIret\fR, char *\fIcp\fR,
+  const struct dns_parse *\fIp\fR);
+.fi
+.RS
+initializes standard RRset fields in \fIret\fR structure using buffer
+pointed to by \fIcp\fR, which should have at least as many bytes
+as \fBdns_stdrr_size\fR(\fIp\fR) returned.  Used to finalize common
+part of type-specific RR structures in parsing routines.
+.RE
+
+.PP
+See library source for usage examples of all the above low-level routines,
+especially source of the parsing routines.
+
+.SS "Auxilary Routines"
+
+.PP
+.nf
+int \fBdns_pton\fR(int \fIaf\fR, const char *\fIsrc\fR, void *\fIdst\fR);
+.fi
+.RS
+privides functionality similar to standard \fBinet_pton\fR() routine,
+to convert printable representation of an IP address of family \fIaf\fR
+(either \fBAF_INET\fR or \fBAF_INET6\fR) pointed to by \fIsrc\fR into
+binary form suitable for socket addresses and transmission over network,
+in buffer pointed to by \fIdst\fR.  The destination buffer should be
+of size 4 for \fBAF_INET\fR family or 16 for \fBAF_INET6\fR.
+The return value is positive on success, 0 if \fIsrc\fR is not a valid text
+representation of an address of family \fIaf\fR, or negative if the
+given address family is not supported.
+.RE
+
+.PP
+.nf
+const char *\fBdns_ntop\fR(int \fIaf\fR, const void *\fIsrc\fR,
+    char *\fIdst\fR, int \fIdstsize\fR)
+.fi
+.RS
+privides functionality similar to standard \fBinet_ntop\fR() routine,
+to convert binary representation of an IP address of family \fIaf\fR
+(either \fBAF_INET\fR or \fBAF_INET6\fR) pointed to by \fIsrc\fR
+(either 4 or 16 bytes) into printable form in buffer in buffer pointed
+to by \fIdst\fR of size \fIdstsize\fR.  The destination buffer should be
+at least of size 16 bytes for \fBAF_INET\fR family or 46 bytes for
+\fBAF_INET6\fR.  The return value is either \fIdst\fR, or NULL pointer
+if \fIdstsize\fR is too small to hold this address or if the given
+address family is not supported.
+.RE
+
+.SH AUTHOR
+.PP
+The \fBudns\fR library has been written by Michael Tokarev, mjt+udns@tls.msk.ru.
+
+.SH VERSION
+.PP
+This manual page corresponds to udns version 0.4, released Jan-2014.
diff --git a/3rdparty/udns/udns.h b/3rdparty/udns/udns.h
new file mode 100644 (file)
index 0000000..371e697
--- /dev/null
@@ -0,0 +1,778 @@
+/* udns.h
+   header file for the UDNS library.
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#ifndef UDNS_VERSION   /* include guard */
+
+#define UDNS_VERSION "0.4"
+
+#ifdef WINDOWS
+# ifdef UDNS_DYNAMIC_LIBRARY
+#  ifdef DNS_LIBRARY_BUILD
+#   define UDNS_API __declspec(dllexport)
+#   define UDNS_DATA_API __declspec(dllexport)
+#  else
+#   define UDNS_API __declspec(dllimport)
+#   define UDNS_DATA_API __declspec(dllimport)
+#  endif
+# endif
+#endif
+
+#ifndef UDNS_API
+# define UDNS_API
+#endif
+#ifndef UDNS_DATA_API
+# define UDNS_DATA_API
+#endif
+
+#include <sys/types.h>         /* for time_t */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* forward declarations if sockets stuff isn't #include'd */
+struct in_addr;
+struct in6_addr;
+struct sockaddr;
+
+/**************************************************************************/
+/**************** Common definitions **************************************/
+
+UDNS_API const char *
+dns_version(void);
+
+struct dns_ctx;
+struct dns_query;
+
+/* shorthand for [const] unsigned char */
+typedef unsigned char dnsc_t;
+typedef const unsigned char dnscc_t;
+
+#define DNS_MAXDN      255     /* max DN length */
+#define DNS_DNPAD      1       /* padding for DN buffers */
+#define DNS_MAXLABEL   63      /* max DN label length */
+#define DNS_MAXNAME    1024    /* max asciiz domain name length */
+#define DNS_HSIZE      12      /* DNS packet header size */
+#define DNS_PORT       53      /* default domain port */
+#define DNS_MAXSERV    6       /* max servers to consult */
+#define DNS_MAXPACKET  512     /* max traditional-DNS UDP packet size */
+#define DNS_EDNS0PACKET        4096    /* EDNS0 packet size to use */
+
+enum dns_class {       /* DNS RR Classes */
+  DNS_C_INVALID        = 0,    /* invalid class */
+  DNS_C_IN     = 1,    /* Internet */
+  DNS_C_CH     = 3,    /* CHAOS */
+  DNS_C_HS     = 4,    /* HESIOD */
+  DNS_C_ANY    = 255   /* wildcard */
+};
+
+enum dns_type {                /* DNS RR Types */
+  DNS_T_INVALID                = 0,    /* Cookie. */
+  DNS_T_A              = 1,    /* Host address. */
+  DNS_T_NS             = 2,    /* Authoritative server. */
+  DNS_T_MD             = 3,    /* Mail destination. */
+  DNS_T_MF             = 4,    /* Mail forwarder. */
+  DNS_T_CNAME          = 5,    /* Canonical name. */
+  DNS_T_SOA            = 6,    /* Start of authority zone. */
+  DNS_T_MB             = 7,    /* Mailbox domain name. */
+  DNS_T_MG             = 8,    /* Mail group member. */
+  DNS_T_MR             = 9,    /* Mail rename name. */
+  DNS_T_NULL           = 10,   /* Null resource record. */
+  DNS_T_WKS            = 11,   /* Well known service. */
+  DNS_T_PTR            = 12,   /* Domain name pointer. */
+  DNS_T_HINFO          = 13,   /* Host information. */
+  DNS_T_MINFO          = 14,   /* Mailbox information. */
+  DNS_T_MX             = 15,   /* Mail routing information. */
+  DNS_T_TXT            = 16,   /* Text strings. */
+  DNS_T_RP             = 17,   /* Responsible person. */
+  DNS_T_AFSDB          = 18,   /* AFS cell database. */
+  DNS_T_X25            = 19,   /* X_25 calling address. */
+  DNS_T_ISDN           = 20,   /* ISDN calling address. */
+  DNS_T_RT             = 21,   /* Router. */
+  DNS_T_NSAP           = 22,   /* NSAP address. */
+  DNS_T_NSAP_PTR       = 23,   /* Reverse NSAP lookup (deprecated). */
+  DNS_T_SIG            = 24,   /* Security signature. */
+  DNS_T_KEY            = 25,   /* Security key. */
+  DNS_T_PX             = 26,   /* X.400 mail mapping. */
+  DNS_T_GPOS           = 27,   /* Geographical position (withdrawn). */
+  DNS_T_AAAA           = 28,   /* Ip6 Address. */
+  DNS_T_LOC            = 29,   /* Location Information. */
+  DNS_T_NXT            = 30,   /* Next domain (security). */
+  DNS_T_EID            = 31,   /* Endpoint identifier. */
+  DNS_T_NIMLOC         = 32,   /* Nimrod Locator. */
+  DNS_T_SRV            = 33,   /* Server Selection. */
+  DNS_T_ATMA           = 34,   /* ATM Address */
+  DNS_T_NAPTR          = 35,   /* Naming Authority PoinTeR */
+  DNS_T_KX             = 36,   /* Key Exchange */
+  DNS_T_CERT           = 37,   /* Certification record */
+  DNS_T_A6             = 38,   /* IPv6 address (deprecates AAAA) */
+  DNS_T_DNAME          = 39,   /* Non-terminal DNAME (for IPv6) */
+  DNS_T_SINK           = 40,   /* Kitchen sink (experimentatl) */
+  DNS_T_OPT            = 41,   /* EDNS0 option (meta-RR) */
+  DNS_T_DS             = 43,   /* DNSSEC */
+  DNS_T_SSHFP          = 44,
+  DNS_T_IPSECKEY       = 45,
+  DNS_T_RRSIG          = 46,   /* DNSSEC */
+  DNS_T_NSEC           = 47,   /* DNSSEC */
+  DNS_T_DNSKEY         = 48,
+  DNS_T_DHCID          = 49,
+  DNS_T_NSEC3          = 50,
+  DNS_T_NSEC3PARAMS    = 51,
+  DNS_T_TALINK         = 58, /* draft-ietf-dnsop-trust-history */
+  DNS_T_SPF            = 99,
+  DNS_T_UINFO          = 100,
+  DNS_T_UID            = 101,
+  DNS_T_GID            = 102,
+  DNS_T_UNSPEC         = 103,
+  DNS_T_TSIG           = 250,  /* Transaction signature. */
+  DNS_T_IXFR           = 251,  /* Incremental zone transfer. */
+  DNS_T_AXFR           = 252,  /* Transfer zone of authority. */
+  DNS_T_MAILB          = 253,  /* Transfer mailbox records. */
+  DNS_T_MAILA          = 254,  /* Transfer mail agent records. */
+  DNS_T_ANY            = 255,  /* Wildcard match. */
+  DNS_T_ZXFR           = 256,  /* BIND-specific, nonstandard. */
+  DNS_T_DLV            = 32769, /* RFC 4431, 5074, DNSSEC Lookaside Validation */
+  DNS_T_MAX            = 65536
+};
+
+/**************************************************************************/
+/**************** Domain Names (DNs) **************************************/
+
+/* return length of the DN */
+UDNS_API unsigned
+dns_dnlen(dnscc_t *dn);
+
+/* return #of labels in a DN */
+UDNS_API unsigned
+dns_dnlabels(dnscc_t *dn);
+
+/* lower- and uppercase single DN char */
+#define DNS_DNLC(c) ((c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 'a' : (c))
+#define DNS_DNUC(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
+
+/* compare the DNs, return dnlen of equal or 0 if not */
+UDNS_API unsigned
+dns_dnequal(dnscc_t *dn1, dnscc_t *dn2);
+
+/* copy one DN to another, size checking */
+UDNS_API unsigned
+dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz);
+
+/* convert asciiz string of length namelen (0 to use strlen) to DN */
+UDNS_API int
+dns_ptodn(const char *name, unsigned namelen,
+          dnsc_t *dn, unsigned dnsiz, int *isabs);
+
+/* simpler form of dns_ptodn() */
+#define dns_sptodn(name,dn,dnsiz) dns_ptodn((name),0,(dn),(dnsiz),0)
+
+UDNS_DATA_API extern dnscc_t dns_inaddr_arpa_dn[14];
+#define DNS_A4RSIZE    30
+UDNS_API int
+dns_a4todn(const struct in_addr *addr, dnscc_t *tdn,
+           dnsc_t *dn, unsigned dnsiz);
+UDNS_API int
+dns_a4ptodn(const struct in_addr *addr, const char *tname,
+            dnsc_t *dn, unsigned dnsiz);
+UDNS_API dnsc_t *
+dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne);
+
+UDNS_DATA_API extern dnscc_t dns_ip6_arpa_dn[10];
+#define DNS_A6RSIZE    74
+UDNS_API int
+dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn,
+           dnsc_t *dn, unsigned dnsiz);
+UDNS_API int
+dns_a6ptodn(const struct in6_addr *addr, const char *tname,
+            dnsc_t *dn, unsigned dnsiz);
+UDNS_API dnsc_t *
+dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne);
+
+/* convert DN into asciiz string */
+UDNS_API int
+dns_dntop(dnscc_t *dn, char *name, unsigned namesiz);
+
+/* convert DN into asciiz string, using static buffer (NOT thread-safe!) */
+UDNS_API const char *
+dns_dntosp(dnscc_t *dn);
+
+/* return buffer size (incl. null byte) required for asciiz form of a DN */
+UDNS_API unsigned
+dns_dntop_size(dnscc_t *dn);
+
+/* either wrappers or reimplementations for inet_ntop() and inet_pton() */
+UDNS_API const char *dns_ntop(int af, const void *src, char *dst, int size);
+UDNS_API int dns_pton(int af, const char *src, void *dst);
+
+/**************************************************************************/
+/**************** DNS raw packet layout ***********************************/
+
+enum dns_rcode {       /* reply codes */
+  DNS_R_NOERROR                = 0,    /* ok, no error */
+  DNS_R_FORMERR                = 1,    /* format error */
+  DNS_R_SERVFAIL       = 2,    /* server failed */
+  DNS_R_NXDOMAIN       = 3,    /* domain does not exists */
+  DNS_R_NOTIMPL                = 4,    /* not implemented */
+  DNS_R_REFUSED                = 5,    /* query refused */
+  /* these are for BIND_UPDATE */
+  DNS_R_YXDOMAIN       = 6,    /* Name exists */
+  DNS_R_YXRRSET                = 7,    /* RRset exists */
+  DNS_R_NXRRSET                = 8,    /* RRset does not exist */
+  DNS_R_NOTAUTH                = 9,    /* Not authoritative for zone */
+  DNS_R_NOTZONE                = 10,   /* Zone of record different from zone section */
+  /*ns_r_max = 11,*/
+  /* The following are TSIG extended errors */
+  DNS_R_BADSIG         = 16,
+  DNS_R_BADKEY         = 17,
+  DNS_R_BADTIME                = 18
+};
+
+static __inline unsigned dns_get16(dnscc_t *s) {
+  return ((unsigned)s[0]<<8) | s[1];
+}
+static __inline unsigned dns_get32(dnscc_t *s) {
+  return ((unsigned)s[0]<<24) | ((unsigned)s[1]<<16)
+        | ((unsigned)s[2]<<8) | s[3];
+}
+static __inline dnsc_t *dns_put16(dnsc_t *d, unsigned n) {
+  *d++ = (dnsc_t)((n >> 8) & 255); *d++ = (dnsc_t)(n & 255); return d;
+}
+static __inline dnsc_t *dns_put32(dnsc_t *d, unsigned n) {
+  *d++ = (dnsc_t)((n >> 24) & 255); *d++ = (dnsc_t)((n >> 16) & 255);
+  *d++ = (dnsc_t)((n >>  8) & 255); *d++ = (dnsc_t)(n & 255);
+  return d;
+}
+
+/* DNS Header layout */
+enum {
+ /* bytes 0:1 - query ID */
+  DNS_H_QID1   = 0,
+  DNS_H_QID2   = 1,
+  DNS_H_QID    = DNS_H_QID1,
+#define dns_qid(pkt)   dns_get16((pkt)+DNS_H_QID)
+ /* byte 2: flags1 */
+  DNS_H_F1     = 2,
+  DNS_HF1_QR   = 0x80, /* query response flag */
+#define dns_qr(pkt)    ((pkt)[DNS_H_F1]&DNS_HF1_QR)
+  DNS_HF1_OPCODE = 0x78, /* opcode, 0 = query */
+#define dns_opcode(pkt)        (((pkt)[DNS_H_F1]&DNS_HF1_OPCODE)>>3)
+  DNS_HF1_AA   = 0x04, /* auth answer */
+#define dns_aa(pkt)    ((pkt)[DNS_H_F1]&DNS_HF1_AA)
+  DNS_HF1_TC   = 0x02, /* truncation flag */
+#define dns_tc(pkt)    ((pkt)[DNS_H_F1]&DNS_HF1_TC)
+  DNS_HF1_RD   = 0x01, /* recursion desired (may be set in query) */
+#define dns_rd(pkt)    ((pkt)[DNS_H_F1]&DNS_HF1_RD)
+ /* byte 3: flags2 */
+  DNS_H_F2     = 3,
+  DNS_HF2_RA   = 0x80, /* recursion available */
+#define dns_ra(pkt)    ((pkt)[DNS_H_F2]&DNS_HF2_RA)
+  DNS_HF2_Z    = 0x40, /* reserved */
+  DNS_HF2_AD   = 0x20, /* DNSSEC: authentic data */
+#define dns_ad(pkt)    ((pkt)[DNS_H_F2]&DNS_HF2_AD)
+  DNS_HF2_CD   = 0x10, /* DNSSEC: checking disabled */
+#define dns_cd(pkt)    ((pkt)[DNS_H_F2]&DNS_HF2_CD)
+  DNS_HF2_RCODE        = 0x0f, /* response code, DNS_R_XXX above */
+#define dns_rcode(pkt) ((pkt)[DNS_H_F2]&DNS_HF2_RCODE)
+ /* bytes 4:5: qdcount, numqueries */
+  DNS_H_QDCNT1 = 4,
+  DNS_H_QDCNT2 = 5,
+  DNS_H_QDCNT  = DNS_H_QDCNT1,
+#define dns_numqd(pkt) dns_get16((pkt)+4)
+ /* bytes 6:7: ancount, numanswers */
+  DNS_H_ANCNT1 = 6,
+  DNS_H_ANCNT2 = 7,
+  DNS_H_ANCNT  = DNS_H_ANCNT1,
+#define dns_numan(pkt) dns_get16((pkt)+6)
+ /* bytes 8:9: nscount, numauthority */
+  DNS_H_NSCNT1 = 8,
+  DNS_H_NSCNT2 = 9,
+  DNS_H_NSCNT  = DNS_H_NSCNT1,
+#define dns_numns(pkt) dns_get16((pkt)+8)
+ /* bytes 10:11: arcount, numadditional */
+  DNS_H_ARCNT1 = 10,
+  DNS_H_ARCNT2 = 11,
+  DNS_H_ARCNT  = DNS_H_ARCNT1,
+#define dns_numar(pkt) dns_get16((pkt)+10)
+#define dns_payload(pkt) ((pkt)+DNS_HSIZE)
+  /* EDNS0 (OPT RR) flags (Ext. Flags) */
+  DNS_EF1_DO   = 0x80, /* DNSSEC OK */
+};
+
+/* packet buffer: start at pkt, end before pkte, current pos *curp.
+ * extract a DN and set *curp to the next byte after DN in packet.
+ * return -1 on error, 0 if dnsiz is too small, or dnlen on ok.
+ */
+UDNS_API int
+dns_getdn(dnscc_t *pkt, dnscc_t **curp, dnscc_t *end,
+          dnsc_t *dn, unsigned dnsiz);
+
+/* skip the DN at position cur in packet ending before pkte,
+ * return pointer to the next byte after the DN or NULL on error */
+UDNS_API dnscc_t *
+dns_skipdn(dnscc_t *end, dnscc_t *cur);
+
+struct dns_rr {                /* DNS Resource Record */
+  dnsc_t dnsrr_dn[DNS_MAXDN];  /* the DN of the RR */
+  enum dns_class dnsrr_cls;    /* Class */
+  enum dns_type  dnsrr_typ;    /* Type */
+  unsigned dnsrr_ttl;          /* Time-To-Live (TTL) */
+  unsigned dnsrr_dsz;          /* data size */
+  dnscc_t *dnsrr_dptr;         /* pointer to start of data */
+  dnscc_t *dnsrr_dend;         /* past end of data */
+};
+
+struct dns_parse {     /* RR/packet parsing state */
+  dnscc_t *dnsp_pkt;           /* start of the packet */
+  dnscc_t *dnsp_end;           /* end of the packet */
+  dnscc_t *dnsp_cur;           /* current packet position */
+  dnscc_t *dnsp_ans;           /* start of answer section */
+  int dnsp_rrl;                        /* number of RRs left to go */
+  int dnsp_nrr;                        /* RR count so far */
+  unsigned dnsp_ttl;           /* TTL value so far */
+  dnscc_t *dnsp_qdn;           /* the RR DN we're looking for */
+  enum dns_class dnsp_qcls;    /* RR class we're looking for or 0 */
+  enum dns_type  dnsp_qtyp;    /* RR type we're looking for or 0 */
+  dnsc_t dnsp_dnbuf[DNS_MAXDN];        /* domain buffer */
+};
+
+/* initialize the parse structure */
+UDNS_API void
+dns_initparse(struct dns_parse *p, dnscc_t *qdn,
+              dnscc_t *pkt, dnscc_t *cur, dnscc_t *end);
+
+/* search next RR, <0=error, 0=no more RRs, >0 = found. */
+UDNS_API int
+dns_nextrr(struct dns_parse *p, struct dns_rr *rr);
+
+UDNS_API void
+dns_rewind(struct dns_parse *p, dnscc_t *qdn);
+
+
+/**************************************************************************/
+/**************** Resolver Context ****************************************/
+
+/* default resolver context */
+UDNS_DATA_API extern struct dns_ctx dns_defctx;
+
+/* reset resolver context to default state, close it if open, drop queries */
+UDNS_API void
+dns_reset(struct dns_ctx *ctx);
+
+/* reset resolver context and read in system configuration */
+UDNS_API int
+dns_init(struct dns_ctx *ctx, int do_open);
+
+/* return new resolver context with the same settings as copy */
+UDNS_API struct dns_ctx *
+dns_new(const struct dns_ctx *copy);
+
+/* free resolver context returned by dns_new(); all queries are dropped */
+UDNS_API void
+dns_free(struct dns_ctx *ctx);
+
+/* add nameserver for a resolver context (or reset nslist if serv==NULL) */
+UDNS_API int
+dns_add_serv(struct dns_ctx *ctx, const char *serv);
+
+/* add nameserver using struct sockaddr structure (with ports) */
+UDNS_API int
+dns_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa);
+
+/* add search list element for a resolver context (or reset it if srch==NULL) */
+UDNS_API int
+dns_add_srch(struct dns_ctx *ctx, const char *srch);
+
+/* set options for a resolver context */
+UDNS_API int
+dns_set_opts(struct dns_ctx *ctx, const char *opts);
+
+enum dns_opt {         /* options */
+  DNS_OPT_FLAGS,       /* flags, DNS_F_XXX */
+  DNS_OPT_TIMEOUT,     /* timeout in secounds */
+  DNS_OPT_NTRIES,      /* number of retries */
+  DNS_OPT_NDOTS,       /* ndots */
+  DNS_OPT_UDPSIZE,     /* EDNS0 UDP size */
+  DNS_OPT_PORT,                /* port to use */
+};
+
+/* set or get (if val<0) an option */
+UDNS_API int
+dns_set_opt(struct dns_ctx *ctx, enum dns_opt opt, int val);
+
+enum dns_flags {
+  DNS_NOSRCH   = 0x00010000,   /* do not perform search */
+  DNS_NORD     = 0x00020000,   /* request no recursion */
+  DNS_AAONLY   = 0x00040000,   /* set AA flag in queries */
+  DNS_SET_DO   = 0x00080000,   /* set EDNS0 "DO" bit (DNSSEC OK) */
+  DNS_SET_CD   = 0x00100000,   /* set CD bit (DNSSEC: checking disabled) */
+};
+
+/* set the debug function pointer */
+typedef void
+(dns_dbgfn)(int code, const struct sockaddr *sa, unsigned salen,
+            dnscc_t *pkt, int plen,
+            const struct dns_query *q, void *data);
+UDNS_API void
+dns_set_dbgfn(struct dns_ctx *ctx, dns_dbgfn *dbgfn);
+
+/* open and return UDP socket */
+UDNS_API int
+dns_open(struct dns_ctx *ctx);
+
+/* return UDP socket or -1 if not open */
+UDNS_API int
+dns_sock(const struct dns_ctx *ctx);
+
+/* close the UDP socket */
+UDNS_API void
+dns_close(struct dns_ctx *ctx);
+
+/* return number of requests queued */
+UDNS_API int
+dns_active(const struct dns_ctx *ctx);
+
+/* return status of the last operation */
+UDNS_API int
+dns_status(const struct dns_ctx *ctx);
+UDNS_API void
+dns_setstatus(struct dns_ctx *ctx, int status);
+
+/* handle I/O event on UDP socket */
+UDNS_API void
+dns_ioevent(struct dns_ctx *ctx, time_t now);
+
+/* process any timeouts, return time in secounds to the
+ * next timeout (or -1 if none) but not greather than maxwait */
+UDNS_API int
+dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now);
+
+/* define timer requesting routine to use */
+typedef void dns_utm_fn(struct dns_ctx *ctx, int timeout, void *data);
+UDNS_API void
+dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data);
+
+/**************************************************************************/
+/**************** Making Queries ******************************************/
+
+/* query callback routine */
+typedef void dns_query_fn(struct dns_ctx *ctx, void *result, void *data);
+
+/* query parse routine: raw DNS => application structure */
+typedef int
+dns_parse_fn(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
+             void **res);
+
+enum dns_status {
+  DNS_E_NOERROR                = 0,    /* ok, not an error */
+  DNS_E_TEMPFAIL       = -1,   /* timeout, SERVFAIL or similar */
+  DNS_E_PROTOCOL       = -2,   /* got garbled reply */
+  DNS_E_NXDOMAIN       = -3,   /* domain does not exists */
+  DNS_E_NODATA         = -4,   /* domain exists but no data of reqd type */
+  DNS_E_NOMEM          = -5,   /* out of memory while processing */
+  DNS_E_BADQUERY       = -6    /* the query is malformed */
+};
+
+/* submit generic DN query */
+UDNS_API struct dns_query *
+dns_submit_dn(struct dns_ctx *ctx,
+              dnscc_t *dn, int qcls, int qtyp, int flags,
+              dns_parse_fn *parse, dns_query_fn *cbck, void *data);
+/* submit generic name query */
+UDNS_API struct dns_query *
+dns_submit_p(struct dns_ctx *ctx,
+             const char *name, int qcls, int qtyp, int flags,
+             dns_parse_fn *parse, dns_query_fn *cbck, void *data);
+
+/* cancel the given async query in progress */
+UDNS_API int
+dns_cancel(struct dns_ctx *ctx, struct dns_query *q);
+
+/* resolve a generic query, return the answer */
+UDNS_API void *
+dns_resolve_dn(struct dns_ctx *ctx,
+               dnscc_t *qdn, int qcls, int qtyp, int flags,
+               dns_parse_fn *parse);
+UDNS_API void *
+dns_resolve_p(struct dns_ctx *ctx,
+              const char *qname, int qcls, int qtyp, int flags,
+              dns_parse_fn *parse);
+UDNS_API void *
+dns_resolve(struct dns_ctx *ctx, struct dns_query *q);
+
+
+/* Specific RR handlers */
+
+#define dns_rr_common(prefix)                                          \
+  char *prefix##_cname;                /* canonical name */                    \
+  char *prefix##_qname;                /* original query name */               \
+  unsigned prefix##_ttl;       /* TTL value */                         \
+  int prefix##_nrr             /* number of records */
+
+struct dns_rr_null {           /* NULL RRset, aka RRset template */
+  dns_rr_common(dnsn);
+};
+
+UDNS_API int
+dns_stdrr_size(const struct dns_parse *p);
+UDNS_API void *
+dns_stdrr_finish(struct dns_rr_null *ret, char *cp, const struct dns_parse *p);
+
+struct dns_rr_a4 {             /* the A RRset */
+  dns_rr_common(dnsa4);
+  struct in_addr *dnsa4_addr;  /* array of addresses, naddr elements */
+};
+
+UDNS_API dns_parse_fn dns_parse_a4;    /* A RR parsing routine */
+typedef void                           /* A query callback routine */
+dns_query_a4_fn(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data);
+
+/* submit A IN query */
+UDNS_API struct dns_query *
+dns_submit_a4(struct dns_ctx *ctx, const char *name, int flags,
+              dns_query_a4_fn *cbck, void *data);
+
+/* resolve A IN query */
+UDNS_API struct dns_rr_a4 *
+dns_resolve_a4(struct dns_ctx *ctx, const char *name, int flags);
+
+
+struct dns_rr_a6 {             /* the AAAA RRset */
+  dns_rr_common(dnsa6);
+  struct in6_addr *dnsa6_addr; /* array of addresses, naddr elements */
+};
+
+UDNS_API dns_parse_fn dns_parse_a6;    /* A RR parsing routine */
+typedef void                           /* A query callback routine */
+dns_query_a6_fn(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data);
+
+/* submit AAAA IN query */
+UDNS_API struct dns_query *
+dns_submit_a6(struct dns_ctx *ctx, const char *name, int flags,
+              dns_query_a6_fn *cbck, void *data);
+
+/* resolve AAAA IN query */
+UDNS_API struct dns_rr_a6 *
+dns_resolve_a6(struct dns_ctx *ctx, const char *name, int flags);
+
+
+struct dns_rr_ptr {            /* the PTR RRset */
+  dns_rr_common(dnsptr);
+  char **dnsptr_ptr;           /* array of PTRs */
+};
+
+UDNS_API dns_parse_fn dns_parse_ptr;   /* PTR RR parsing routine */
+typedef void                           /* PTR query callback */
+dns_query_ptr_fn(struct dns_ctx *ctx, struct dns_rr_ptr *result, void *data);
+/* submit PTR IN in-addr.arpa query */
+UDNS_API struct dns_query *
+dns_submit_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr,
+                 dns_query_ptr_fn *cbck, void *data);
+/* resolve PTR IN in-addr.arpa query */
+UDNS_API struct dns_rr_ptr *
+dns_resolve_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr);
+
+/* the same as above, but for ip6.arpa */
+UDNS_API struct dns_query *
+dns_submit_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr,
+                 dns_query_ptr_fn *cbck, void *data);
+UDNS_API struct dns_rr_ptr *
+dns_resolve_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr);
+
+
+struct dns_mx {                /* single MX RR */
+  int priority;                /* MX priority */
+  char *name;          /* MX name */
+};
+struct dns_rr_mx {             /* the MX RRset */
+  dns_rr_common(dnsmx);
+  struct dns_mx *dnsmx_mx;     /* array of MXes */
+};
+UDNS_API dns_parse_fn dns_parse_mx;    /* MX RR parsing routine */
+typedef void                           /* MX RR callback */
+dns_query_mx_fn(struct dns_ctx *ctx, struct dns_rr_mx *result, void *data);
+/* submit MX IN query */
+UDNS_API struct dns_query *
+dns_submit_mx(struct dns_ctx *ctx, const char *name, int flags,
+              dns_query_mx_fn *cbck, void *data);
+/* resolve MX IN query */
+UDNS_API struct dns_rr_mx *
+dns_resolve_mx(struct dns_ctx *ctx, const char *name, int flags);
+
+
+struct dns_txt {       /* single TXT record */
+  int len;             /* length of the text */
+  dnsc_t *txt; /* pointer to text buffer. May contain nulls. */
+};
+struct dns_rr_txt {            /* the TXT RRset */
+  dns_rr_common(dnstxt);
+  struct dns_txt *dnstxt_txt;  /* array of TXT records */
+};
+UDNS_API dns_parse_fn dns_parse_txt;   /* TXT RR parsing routine */
+typedef void                           /* TXT RR callback */
+dns_query_txt_fn(struct dns_ctx *ctx, struct dns_rr_txt *result, void *data);
+/* submit TXT query */
+UDNS_API struct dns_query *
+dns_submit_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags,
+               dns_query_txt_fn *cbck, void *data);
+/* resolve TXT query */
+UDNS_API struct dns_rr_txt *
+dns_resolve_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags);
+
+
+struct dns_srv {       /* single SRV RR */
+  int priority;                /* SRV priority */
+  int weight;          /* SRV weight */
+  int port;            /* SRV port */
+  char *name;          /* SRV name */
+};
+struct dns_rr_srv {            /* the SRV RRset */
+  dns_rr_common(dnssrv);
+  struct dns_srv *dnssrv_srv;  /* array of SRVes */
+};
+UDNS_API dns_parse_fn dns_parse_srv;   /* SRV RR parsing routine */
+typedef void                           /* SRV RR callback */
+dns_query_srv_fn(struct dns_ctx *ctx, struct dns_rr_srv *result, void *data);
+/* submit SRV IN query */
+UDNS_API struct dns_query *
+dns_submit_srv(struct dns_ctx *ctx,
+               const char *name, const char *srv, const char *proto,
+               int flags, dns_query_srv_fn *cbck, void *data);
+/* resolve SRV IN query */
+UDNS_API struct dns_rr_srv *
+dns_resolve_srv(struct dns_ctx *ctx,
+                const char *name, const char *srv, const char *proto,
+                int flags);
+
+/* NAPTR (RFC3403) RR type */
+struct dns_naptr {     /* single NAPTR RR */
+  int order;           /* NAPTR order */
+  int preference;      /* NAPTR preference */
+  char *flags;         /* NAPTR flags */
+  char *service;       /* NAPTR service */
+  char *regexp;                /* NAPTR regexp */
+  char *replacement;   /* NAPTR replacement */
+};
+
+struct dns_rr_naptr {          /* the NAPTR RRset */
+  dns_rr_common(dnsnaptr);
+  struct dns_naptr *dnsnaptr_naptr;    /* array of NAPTRes */
+};
+UDNS_API dns_parse_fn dns_parse_naptr; /* NAPTR RR parsing routine */
+typedef void                           /* NAPTR RR callback */
+dns_query_naptr_fn(struct dns_ctx *ctx,
+                   struct dns_rr_naptr *result, void *data);
+/* submit NAPTR IN query */
+UDNS_API struct dns_query *
+dns_submit_naptr(struct dns_ctx *ctx, const char *name, int flags,
+                 dns_query_naptr_fn *cbck, void *data);
+/* resolve NAPTR IN query */
+UDNS_API struct dns_rr_naptr *
+dns_resolve_naptr(struct dns_ctx *ctx, const char *name, int flags);
+
+
+UDNS_API struct dns_query *
+dns_submit_a4dnsbl(struct dns_ctx *ctx,
+                   const struct in_addr *addr, const char *dnsbl,
+                   dns_query_a4_fn *cbck, void *data);
+UDNS_API struct dns_query *
+dns_submit_a4dnsbl_txt(struct dns_ctx *ctx,
+                       const struct in_addr *addr, const char *dnsbl,
+                       dns_query_txt_fn *cbck, void *data);
+UDNS_API struct dns_rr_a4 *
+dns_resolve_a4dnsbl(struct dns_ctx *ctx,
+                    const struct in_addr *addr, const char *dnsbl);
+UDNS_API struct dns_rr_txt *
+dns_resolve_a4dnsbl_txt(struct dns_ctx *ctx,
+                        const struct in_addr *addr, const char *dnsbl);
+
+UDNS_API struct dns_query *
+dns_submit_a6dnsbl(struct dns_ctx *ctx,
+                   const struct in6_addr *addr, const char *dnsbl,
+                   dns_query_a4_fn *cbck, void *data);
+UDNS_API struct dns_query *
+dns_submit_a6dnsbl_txt(struct dns_ctx *ctx,
+                       const struct in6_addr *addr, const char *dnsbl,
+                       dns_query_txt_fn *cbck, void *data);
+UDNS_API struct dns_rr_a4 *
+dns_resolve_a6dnsbl(struct dns_ctx *ctx,
+                    const struct in6_addr *addr, const char *dnsbl);
+UDNS_API struct dns_rr_txt *
+dns_resolve_a6dnsbl_txt(struct dns_ctx *ctx,
+                        const struct in6_addr *addr, const char *dnsbl);
+
+UDNS_API struct dns_query *
+dns_submit_rhsbl(struct dns_ctx *ctx,
+                 const char *name, const char *rhsbl,
+                 dns_query_a4_fn *cbck, void *data);
+UDNS_API struct dns_query *
+dns_submit_rhsbl_txt(struct dns_ctx *ctx,
+                     const char *name, const char *rhsbl,
+                     dns_query_txt_fn *cbck, void *data);
+UDNS_API struct dns_rr_a4 *
+dns_resolve_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl);
+UDNS_API struct dns_rr_txt *
+dns_resolve_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl);
+
+/**************************************************************************/
+/**************** Names, Names ********************************************/
+
+struct dns_nameval {
+  int val;
+  const char *name;
+};
+
+UDNS_DATA_API extern const struct dns_nameval dns_classtab[];
+UDNS_DATA_API extern const struct dns_nameval dns_typetab[];
+UDNS_DATA_API extern const struct dns_nameval dns_rcodetab[];
+UDNS_API int
+dns_findname(const struct dns_nameval *nv, const char *name);
+#define dns_findclassname(cls) dns_findname(dns_classtab, (cls))
+#define dns_findtypename(type) dns_findname(dns_typetab, (type))
+#define dns_findrcodename(rcode) dns_findname(dns_rcodetab, (rcode))
+
+UDNS_API const char *dns_classname(enum dns_class cls);
+UDNS_API const char *dns_typename(enum dns_type type);
+UDNS_API const char *dns_rcodename(enum dns_rcode rcode);
+const char *_dns_format_code(char *buf, const char *prefix, int code);
+
+UDNS_API const char *dns_strerror(int errnum);
+
+/* simple pseudo-random number generator, code by Bob Jenkins */
+
+struct udns_jranctx {  /* the context */
+  unsigned a, b, c, d;
+};
+
+/* initialize the RNG with a given seed */
+UDNS_API void
+udns_jraninit(struct udns_jranctx *x, unsigned seed);
+
+/* return next random number.  32bits on most platforms so far. */
+UDNS_API unsigned
+udns_jranval(struct udns_jranctx *x);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* include guard */
diff --git a/3rdparty/udns/udns_XtoX.c b/3rdparty/udns/udns_XtoX.c
new file mode 100644 (file)
index 0000000..60e3fdf
--- /dev/null
@@ -0,0 +1,50 @@
+/* udns_XtoX.c
+   udns_ntop() and udns_pton() routines, which are either
+     - wrappers for inet_ntop() and inet_pton() or
+     - reimplementations of those routines.
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "udns.h"
+
+#ifdef HAVE_INET_PTON_NTOP
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+const char *dns_ntop(int af, const void *src, char *dst, int size) {
+  return inet_ntop(af, src, dst, size);
+}
+
+int dns_pton(int af, const char *src, void *dst) {
+  return inet_pton(af, src, dst);
+}
+
+#else
+
+#define inet_XtoX_prefix udns_
+#include "inet_XtoX.c"
+
+#endif
diff --git a/3rdparty/udns/udns_bl.c b/3rdparty/udns/udns_bl.c
new file mode 100644 (file)
index 0000000..f6be393
--- /dev/null
@@ -0,0 +1,160 @@
+/* udns_bl.c
+   DNSBL stuff
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include "udns.h"
+#ifndef NULL
+# define NULL 0
+#endif
+
+struct dns_query *
+dns_submit_a4dnsbl(struct dns_ctx *ctx,
+                   const struct in_addr *addr, const char *dnsbl,
+                   dns_query_a4_fn *cbck, void *data) {
+  dnsc_t dn[DNS_MAXDN];
+  if (dns_a4ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) {
+    dns_setstatus(ctx, DNS_E_BADQUERY);
+    return NULL;
+  }
+  return
+    dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH,
+                  dns_parse_a4, (dns_query_fn*)cbck, data);
+}
+
+struct dns_query *
+dns_submit_a4dnsbl_txt(struct dns_ctx *ctx,
+                       const struct in_addr *addr, const char *dnsbl,
+                       dns_query_txt_fn *cbck, void *data) {
+  dnsc_t dn[DNS_MAXDN];
+  if (dns_a4ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) {
+    dns_setstatus(ctx, DNS_E_BADQUERY);
+    return NULL;
+  }
+  return
+    dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH,
+                  dns_parse_txt, (dns_query_fn*)cbck, data);
+}
+
+struct dns_rr_a4 *
+dns_resolve_a4dnsbl(struct dns_ctx *ctx,
+                    const struct in_addr *addr, const char *dnsbl) {
+  return (struct dns_rr_a4 *)
+    dns_resolve(ctx, dns_submit_a4dnsbl(ctx, addr, dnsbl, 0, 0));
+}
+
+struct dns_rr_txt *
+dns_resolve_a4dnsbl_txt(struct dns_ctx *ctx,
+                        const struct in_addr *addr, const char *dnsbl) {
+  return (struct dns_rr_txt *)
+    dns_resolve(ctx, dns_submit_a4dnsbl_txt(ctx, addr, dnsbl, 0, 0));
+}
+
+
+struct dns_query *
+dns_submit_a6dnsbl(struct dns_ctx *ctx,
+                   const struct in6_addr *addr, const char *dnsbl,
+                   dns_query_a4_fn *cbck, void *data) {
+  dnsc_t dn[DNS_MAXDN];
+  if (dns_a6ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) {
+    dns_setstatus(ctx, DNS_E_BADQUERY);
+    return NULL;
+  }
+  return
+    dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH,
+                  dns_parse_a4, (dns_query_fn*)cbck, data);
+}
+
+struct dns_query *
+dns_submit_a6dnsbl_txt(struct dns_ctx *ctx,
+                       const struct in6_addr *addr, const char *dnsbl,
+                       dns_query_txt_fn *cbck, void *data) {
+  dnsc_t dn[DNS_MAXDN];
+  if (dns_a6ptodn(addr, dnsbl, dn, sizeof(dn)) <= 0) {
+    dns_setstatus(ctx, DNS_E_BADQUERY);
+    return NULL;
+  }
+  return
+    dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH,
+                  dns_parse_txt, (dns_query_fn*)cbck, data);
+}
+
+struct dns_rr_a4 *
+dns_resolve_a6dnsbl(struct dns_ctx *ctx,
+                    const struct in6_addr *addr, const char *dnsbl) {
+  return (struct dns_rr_a4 *)
+    dns_resolve(ctx, dns_submit_a6dnsbl(ctx, addr, dnsbl, 0, 0));
+}
+
+struct dns_rr_txt *
+dns_resolve_a6dnsbl_txt(struct dns_ctx *ctx,
+                        const struct in6_addr *addr, const char *dnsbl) {
+  return (struct dns_rr_txt *)
+    dns_resolve(ctx, dns_submit_a6dnsbl_txt(ctx, addr, dnsbl, 0, 0));
+}
+
+static int
+dns_rhsbltodn(const char *name, const char *rhsbl, dnsc_t dn[DNS_MAXDN])
+{
+  int l = dns_sptodn(name, dn, DNS_MAXDN);
+  if (l <= 0) return 0;
+  l = dns_sptodn(rhsbl, dn+l-1, DNS_MAXDN-l+1);
+  if (l <= 0) return 0;
+  return 1;
+}
+
+struct dns_query *
+dns_submit_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl,
+                 dns_query_a4_fn *cbck, void *data) {
+  dnsc_t dn[DNS_MAXDN];
+  if (!dns_rhsbltodn(name, rhsbl, dn)) {
+    dns_setstatus(ctx, DNS_E_BADQUERY);
+    return NULL;
+  }
+  return
+    dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_A, DNS_NOSRCH,
+                  dns_parse_a4, (dns_query_fn*)cbck, data);
+}
+struct dns_query *
+dns_submit_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl,
+                     dns_query_txt_fn *cbck, void *data) {
+  dnsc_t dn[DNS_MAXDN];
+  if (!dns_rhsbltodn(name, rhsbl, dn)) {
+    dns_setstatus(ctx, DNS_E_BADQUERY);
+    return NULL;
+  }
+  return
+    dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_TXT, DNS_NOSRCH,
+                  dns_parse_txt, (dns_query_fn*)cbck, data);
+}
+
+struct dns_rr_a4 *
+dns_resolve_rhsbl(struct dns_ctx *ctx, const char *name, const char *rhsbl) {
+  return (struct dns_rr_a4*)
+    dns_resolve(ctx, dns_submit_rhsbl(ctx, name, rhsbl, 0, 0));
+}
+
+struct dns_rr_txt *
+dns_resolve_rhsbl_txt(struct dns_ctx *ctx, const char *name, const char *rhsbl)
+{
+  return (struct dns_rr_txt*)
+    dns_resolve(ctx, dns_submit_rhsbl_txt(ctx, name, rhsbl, 0, 0));
+}
diff --git a/3rdparty/udns/udns_codes.c b/3rdparty/udns/udns_codes.c
new file mode 100644 (file)
index 0000000..c637e98
--- /dev/null
@@ -0,0 +1,199 @@
+/* Automatically generated. */
+#include "udns.h"
+
+const struct dns_nameval dns_typetab[] = {
+ {DNS_T_INVALID,"INVALID"},
+ {DNS_T_A,"A"},
+ {DNS_T_NS,"NS"},
+ {DNS_T_MD,"MD"},
+ {DNS_T_MF,"MF"},
+ {DNS_T_CNAME,"CNAME"},
+ {DNS_T_SOA,"SOA"},
+ {DNS_T_MB,"MB"},
+ {DNS_T_MG,"MG"},
+ {DNS_T_MR,"MR"},
+ {DNS_T_NULL,"NULL"},
+ {DNS_T_WKS,"WKS"},
+ {DNS_T_PTR,"PTR"},
+ {DNS_T_HINFO,"HINFO"},
+ {DNS_T_MINFO,"MINFO"},
+ {DNS_T_MX,"MX"},
+ {DNS_T_TXT,"TXT"},
+ {DNS_T_RP,"RP"},
+ {DNS_T_AFSDB,"AFSDB"},
+ {DNS_T_X25,"X25"},
+ {DNS_T_ISDN,"ISDN"},
+ {DNS_T_RT,"RT"},
+ {DNS_T_NSAP,"NSAP"},
+ {DNS_T_NSAP_PTR,"NSAP_PTR"},
+ {DNS_T_SIG,"SIG"},
+ {DNS_T_KEY,"KEY"},
+ {DNS_T_PX,"PX"},
+ {DNS_T_GPOS,"GPOS"},
+ {DNS_T_AAAA,"AAAA"},
+ {DNS_T_LOC,"LOC"},
+ {DNS_T_NXT,"NXT"},
+ {DNS_T_EID,"EID"},
+ {DNS_T_NIMLOC,"NIMLOC"},
+ {DNS_T_SRV,"SRV"},
+ {DNS_T_ATMA,"ATMA"},
+ {DNS_T_NAPTR,"NAPTR"},
+ {DNS_T_KX,"KX"},
+ {DNS_T_CERT,"CERT"},
+ {DNS_T_A6,"A6"},
+ {DNS_T_DNAME,"DNAME"},
+ {DNS_T_SINK,"SINK"},
+ {DNS_T_OPT,"OPT"},
+ {DNS_T_DS,"DS"},
+ {DNS_T_SSHFP,"SSHFP"},
+ {DNS_T_IPSECKEY,"IPSECKEY"},
+ {DNS_T_RRSIG,"RRSIG"},
+ {DNS_T_NSEC,"NSEC"},
+ {DNS_T_DNSKEY,"DNSKEY"},
+ {DNS_T_DHCID,"DHCID"},
+ {DNS_T_NSEC3,"NSEC3"},
+ {DNS_T_NSEC3PARAMS,"NSEC3PARAMS"},
+ {DNS_T_TALINK,"TALINK"},
+ {DNS_T_SPF,"SPF"},
+ {DNS_T_UINFO,"UINFO"},
+ {DNS_T_UID,"UID"},
+ {DNS_T_GID,"GID"},
+ {DNS_T_UNSPEC,"UNSPEC"},
+ {DNS_T_TSIG,"TSIG"},
+ {DNS_T_IXFR,"IXFR"},
+ {DNS_T_AXFR,"AXFR"},
+ {DNS_T_MAILB,"MAILB"},
+ {DNS_T_MAILA,"MAILA"},
+ {DNS_T_ANY,"ANY"},
+ {DNS_T_ZXFR,"ZXFR"},
+ {DNS_T_DLV,"DLV"},
+ {DNS_T_MAX,"MAX"},
+ {0,0}};
+const char *dns_typename(enum dns_type code) {
+ static char nm[20];
+ switch(code) {
+ case DNS_T_INVALID: return dns_typetab[0].name;
+ case DNS_T_A: return dns_typetab[1].name;
+ case DNS_T_NS: return dns_typetab[2].name;
+ case DNS_T_MD: return dns_typetab[3].name;
+ case DNS_T_MF: return dns_typetab[4].name;
+ case DNS_T_CNAME: return dns_typetab[5].name;
+ case DNS_T_SOA: return dns_typetab[6].name;
+ case DNS_T_MB: return dns_typetab[7].name;
+ case DNS_T_MG: return dns_typetab[8].name;
+ case DNS_T_MR: return dns_typetab[9].name;
+ case DNS_T_NULL: return dns_typetab[10].name;
+ case DNS_T_WKS: return dns_typetab[11].name;
+ case DNS_T_PTR: return dns_typetab[12].name;
+ case DNS_T_HINFO: return dns_typetab[13].name;
+ case DNS_T_MINFO: return dns_typetab[14].name;
+ case DNS_T_MX: return dns_typetab[15].name;
+ case DNS_T_TXT: return dns_typetab[16].name;
+ case DNS_T_RP: return dns_typetab[17].name;
+ case DNS_T_AFSDB: return dns_typetab[18].name;
+ case DNS_T_X25: return dns_typetab[19].name;
+ case DNS_T_ISDN: return dns_typetab[20].name;
+ case DNS_T_RT: return dns_typetab[21].name;
+ case DNS_T_NSAP: return dns_typetab[22].name;
+ case DNS_T_NSAP_PTR: return dns_typetab[23].name;
+ case DNS_T_SIG: return dns_typetab[24].name;
+ case DNS_T_KEY: return dns_typetab[25].name;
+ case DNS_T_PX: return dns_typetab[26].name;
+ case DNS_T_GPOS: return dns_typetab[27].name;
+ case DNS_T_AAAA: return dns_typetab[28].name;
+ case DNS_T_LOC: return dns_typetab[29].name;
+ case DNS_T_NXT: return dns_typetab[30].name;
+ case DNS_T_EID: return dns_typetab[31].name;
+ case DNS_T_NIMLOC: return dns_typetab[32].name;
+ case DNS_T_SRV: return dns_typetab[33].name;
+ case DNS_T_ATMA: return dns_typetab[34].name;
+ case DNS_T_NAPTR: return dns_typetab[35].name;
+ case DNS_T_KX: return dns_typetab[36].name;
+ case DNS_T_CERT: return dns_typetab[37].name;
+ case DNS_T_A6: return dns_typetab[38].name;
+ case DNS_T_DNAME: return dns_typetab[39].name;
+ case DNS_T_SINK: return dns_typetab[40].name;
+ case DNS_T_OPT: return dns_typetab[41].name;
+ case DNS_T_DS: return dns_typetab[42].name;
+ case DNS_T_SSHFP: return dns_typetab[43].name;
+ case DNS_T_IPSECKEY: return dns_typetab[44].name;
+ case DNS_T_RRSIG: return dns_typetab[45].name;
+ case DNS_T_NSEC: return dns_typetab[46].name;
+ case DNS_T_DNSKEY: return dns_typetab[47].name;
+ case DNS_T_DHCID: return dns_typetab[48].name;
+ case DNS_T_NSEC3: return dns_typetab[49].name;
+ case DNS_T_NSEC3PARAMS: return dns_typetab[50].name;
+ case DNS_T_TALINK: return dns_typetab[51].name;
+ case DNS_T_SPF: return dns_typetab[52].name;
+ case DNS_T_UINFO: return dns_typetab[53].name;
+ case DNS_T_UID: return dns_typetab[54].name;
+ case DNS_T_GID: return dns_typetab[55].name;
+ case DNS_T_UNSPEC: return dns_typetab[56].name;
+ case DNS_T_TSIG: return dns_typetab[57].name;
+ case DNS_T_IXFR: return dns_typetab[58].name;
+ case DNS_T_AXFR: return dns_typetab[59].name;
+ case DNS_T_MAILB: return dns_typetab[60].name;
+ case DNS_T_MAILA: return dns_typetab[61].name;
+ case DNS_T_ANY: return dns_typetab[62].name;
+ case DNS_T_ZXFR: return dns_typetab[63].name;
+ case DNS_T_DLV: return dns_typetab[64].name;
+ case DNS_T_MAX: return dns_typetab[65].name;
+ }
+ return _dns_format_code(nm,"type",code);
+}
+
+const struct dns_nameval dns_classtab[] = {
+ {DNS_C_INVALID,"INVALID"},
+ {DNS_C_IN,"IN"},
+ {DNS_C_CH,"CH"},
+ {DNS_C_HS,"HS"},
+ {DNS_C_ANY,"ANY"},
+ {0,0}};
+const char *dns_classname(enum dns_class code) {
+ static char nm[20];
+ switch(code) {
+ case DNS_C_INVALID: return dns_classtab[0].name;
+ case DNS_C_IN: return dns_classtab[1].name;
+ case DNS_C_CH: return dns_classtab[2].name;
+ case DNS_C_HS: return dns_classtab[3].name;
+ case DNS_C_ANY: return dns_classtab[4].name;
+ }
+ return _dns_format_code(nm,"class",code);
+}
+
+const struct dns_nameval dns_rcodetab[] = {
+ {DNS_R_NOERROR,"NOERROR"},
+ {DNS_R_FORMERR,"FORMERR"},
+ {DNS_R_SERVFAIL,"SERVFAIL"},
+ {DNS_R_NXDOMAIN,"NXDOMAIN"},
+ {DNS_R_NOTIMPL,"NOTIMPL"},
+ {DNS_R_REFUSED,"REFUSED"},
+ {DNS_R_YXDOMAIN,"YXDOMAIN"},
+ {DNS_R_YXRRSET,"YXRRSET"},
+ {DNS_R_NXRRSET,"NXRRSET"},
+ {DNS_R_NOTAUTH,"NOTAUTH"},
+ {DNS_R_NOTZONE,"NOTZONE"},
+ {DNS_R_BADSIG,"BADSIG"},
+ {DNS_R_BADKEY,"BADKEY"},
+ {DNS_R_BADTIME,"BADTIME"},
+ {0,0}};
+const char *dns_rcodename(enum dns_rcode code) {
+ static char nm[20];
+ switch(code) {
+ case DNS_R_NOERROR: return dns_rcodetab[0].name;
+ case DNS_R_FORMERR: return dns_rcodetab[1].name;
+ case DNS_R_SERVFAIL: return dns_rcodetab[2].name;
+ case DNS_R_NXDOMAIN: return dns_rcodetab[3].name;
+ case DNS_R_NOTIMPL: return dns_rcodetab[4].name;
+ case DNS_R_REFUSED: return dns_rcodetab[5].name;
+ case DNS_R_YXDOMAIN: return dns_rcodetab[6].name;
+ case DNS_R_YXRRSET: return dns_rcodetab[7].name;
+ case DNS_R_NXRRSET: return dns_rcodetab[8].name;
+ case DNS_R_NOTAUTH: return dns_rcodetab[9].name;
+ case DNS_R_NOTZONE: return dns_rcodetab[10].name;
+ case DNS_R_BADSIG: return dns_rcodetab[11].name;
+ case DNS_R_BADKEY: return dns_rcodetab[12].name;
+ case DNS_R_BADTIME: return dns_rcodetab[13].name;
+ }
+ return _dns_format_code(nm,"rcode",code);
+}
diff --git a/3rdparty/udns/udns_dn.c b/3rdparty/udns/udns_dn.c
new file mode 100644 (file)
index 0000000..ae3fd17
--- /dev/null
@@ -0,0 +1,379 @@
+/* udns_dn.c
+   domain names manipulation routines
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include <string.h>
+#include "udns.h"
+
+unsigned dns_dnlen(dnscc_t *dn) {
+  register dnscc_t *d = dn;
+  while(*d)
+    d += 1 + *d;
+  return (unsigned)(d - dn) + 1;
+}
+
+unsigned dns_dnlabels(register dnscc_t *dn) {
+  register unsigned l = 0;
+  while(*dn)
+    ++l, dn += 1 + *dn;
+  return l;
+}
+
+unsigned dns_dnequal(register dnscc_t *dn1, register dnscc_t *dn2) {
+  register unsigned c;
+  dnscc_t *dn = dn1;
+  for(;;) {
+    if ((c = *dn1++) != *dn2++)
+      return 0;
+    if (!c)
+      return (unsigned)(dn1 - dn);
+    while(c--) {
+      if (DNS_DNLC(*dn1) != DNS_DNLC(*dn2))
+        return 0;
+      ++dn1; ++dn2;
+    }
+  }
+}
+
+unsigned
+dns_dntodn(dnscc_t *sdn, dnsc_t *ddn, unsigned ddnsiz) {
+  unsigned sdnlen = dns_dnlen(sdn);
+  if (ddnsiz < sdnlen)
+    return 0;
+  memcpy(ddn, sdn, sdnlen);
+  return sdnlen;
+}
+
+int
+dns_ptodn(const char *name, unsigned namelen,
+          dnsc_t *dn, unsigned dnsiz, int *isabs)
+{
+  dnsc_t *dp;          /* current position in dn (len byte first) */
+  dnsc_t *const de     /* end of dn: last byte that can be filled up */
+      = dn + (dnsiz >= DNS_MAXDN ? DNS_MAXDN : dnsiz) - 1;
+  dnscc_t *np = (dnscc_t *)name;
+  dnscc_t *ne = np + (namelen ? namelen : strlen((char*)np));
+  dnsc_t *llab;                /* start of last label (llab[-1] will be length) */
+  unsigned c;          /* next input character, or length of last label */
+
+  if (!dnsiz)
+    return 0;
+  dp = llab = dn + 1;
+
+  while(np < ne) {
+
+    if (*np == '.') {  /* label delimiter */
+      c = dp - llab;           /* length of the label */
+      if (!c) {                        /* empty label */
+        if (np == (dnscc_t *)name && np + 1 == ne) {
+          /* special case for root dn, aka `.' */
+          ++np;
+          break;
+        }
+        return -1;             /* zero label */
+      }
+      if (c > DNS_MAXLABEL)
+        return -1;             /* label too long */
+      llab[-1] = (dnsc_t)c;    /* update len of last label */
+      llab = ++dp; /* start new label, llab[-1] will be len of it */
+      ++np;
+      continue;
+    }
+
+    /* check whenever we may put out one more byte */
+    if (dp >= de) /* too long? */
+      return dnsiz >= DNS_MAXDN ? -1 : 0;
+    if (*np != '\\') { /* non-escape, simple case */
+      *dp++ = *np++;
+      continue;
+    }
+    /* handle \-style escape */
+    /* note that traditionally, domain names (gethostbyname etc)
+     * used decimal \dd notation, not octal \ooo (RFC1035), so
+     * we're following this tradition here.
+     */
+    if (++np == ne)
+      return -1;                       /* bad escape */
+    else if (*np >= '0' && *np <= '9') { /* decimal number */
+      /* we allow not only exactly 3 digits as per RFC1035,
+       * but also 2 or 1, for better usability. */
+      c = *np++ - '0';
+      if (np < ne && *np >= '0' && *np <= '9') { /* 2digits */
+        c = c * 10 + *np++ - '0';
+        if (np < ne && *np >= '0' && *np <= '9') {
+          c = c * 10 + *np++ - '0';
+          if (c > 255)
+            return -1;                 /* bad escape */
+        }
+      }
+    }
+    else
+      c = *np++;
+    *dp++ = (dnsc_t)c; /* place next out byte */
+  }
+
+  if ((c = dp - llab) > DNS_MAXLABEL)
+    return -1;                         /* label too long */
+  if ((llab[-1] = (dnsc_t)c) != 0) {
+    *dp++ = 0;
+    if (isabs)
+      *isabs = 0;
+  }
+  else if (isabs)
+    *isabs = 1;
+
+  return dp - dn;
+}
+
+dnscc_t dns_inaddr_arpa_dn[14] = "\07in-addr\04arpa";
+
+dnsc_t *
+dns_a4todn_(const struct in_addr *addr, dnsc_t *dn, dnsc_t *dne) {
+  const unsigned char *s = ((const unsigned char *)addr) + 4;
+  while(s > (const unsigned char *)addr) {
+    unsigned n = *--s;
+    dnsc_t *p = dn + 1;
+    if (n > 99) {
+      if (p + 2 > dne) return 0;
+      *p++ = n / 100 + '0';
+      *p++ = (n % 100 / 10) + '0';
+      *p = n % 10 + '0';
+    }
+    else if (n > 9) {
+      if (p + 1 > dne) return 0;
+      *p++ = n / 10 + '0';
+      *p = n % 10 + '0';
+    }
+    else {
+      if (p > dne) return 0;
+      *p = n + '0';
+    }
+    *dn = p - dn;
+    dn = p + 1;
+  }
+  return dn;
+}
+
+int dns_a4todn(const struct in_addr *addr, dnscc_t *tdn,
+               dnsc_t *dn, unsigned dnsiz) {
+  dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz);
+  dnsc_t *p;
+  unsigned l;
+  p = dns_a4todn_(addr, dn, dne);
+  if (!p) return 0;
+  if (!tdn)
+    tdn = dns_inaddr_arpa_dn;
+  l = dns_dnlen(tdn);
+  if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0;
+  memcpy(p, tdn, l);
+  return (p + l) - dn;
+}
+
+int dns_a4ptodn(const struct in_addr *addr, const char *tname,
+                dnsc_t *dn, unsigned dnsiz) {
+  dnsc_t *p;
+  int r;
+  if (!tname)
+    return dns_a4todn(addr, NULL, dn, dnsiz);
+  p = dns_a4todn_(addr, dn, dn + dnsiz);
+  if (!p) return 0;
+  r = dns_sptodn(tname, p, dnsiz - (p - dn));
+  return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0;
+}
+
+dnscc_t dns_ip6_arpa_dn[10] = "\03ip6\04arpa";
+
+dnsc_t *
+dns_a6todn_(const struct in6_addr *addr, dnsc_t *dn, dnsc_t *dne) {
+  const unsigned char *s = ((const unsigned char *)addr) + 16;
+  if (dn + 64 > dne) return 0;
+  while(s > (const unsigned char *)addr) {
+    unsigned n = *--s & 0x0f;
+    *dn++ = 1;
+    *dn++ = n > 9 ? n + 'a' - 10 : n + '0';
+    *dn++ = 1;
+    n = *s >> 4;
+    *dn++ = n > 9 ? n + 'a' - 10 : n + '0';
+  }
+  return dn;
+}
+
+int dns_a6todn(const struct in6_addr *addr, dnscc_t *tdn,
+               dnsc_t *dn, unsigned dnsiz) {
+  dnsc_t *dne = dn + (dnsiz > DNS_MAXDN ? DNS_MAXDN : dnsiz);
+  dnsc_t *p;
+  unsigned l;
+  p = dns_a6todn_(addr, dn, dne);
+  if (!p) return 0;
+  if (!tdn)
+    tdn = dns_ip6_arpa_dn;
+  l = dns_dnlen(tdn);
+  if (p + l > dne) return dnsiz >= DNS_MAXDN ? -1 : 0;
+  memcpy(p, tdn, l);
+  return (p + l) - dn;
+}
+
+int dns_a6ptodn(const struct in6_addr *addr, const char *tname,
+                dnsc_t *dn, unsigned dnsiz) {
+  dnsc_t *p;
+  int r;
+  if (!tname)
+    return dns_a6todn(addr, NULL, dn, dnsiz);
+  p = dns_a6todn_(addr, dn, dn + dnsiz);
+  if (!p) return 0;
+  r = dns_sptodn(tname, p, dnsiz - (p - dn));
+  return r != 0 ? r : dnsiz >= DNS_MAXDN ? -1 : 0;
+}
+
+/* return size of buffer required to convert the dn into asciiz string.
+ * Keep in sync with dns_dntop() below.
+ */
+unsigned dns_dntop_size(dnscc_t *dn) {
+  unsigned size = 0;           /* the size reqd */
+  dnscc_t *le;                 /* label end */
+
+  while(*dn) {
+    /* *dn is the length of the next label, non-zero */
+    if (size)
+      ++size;          /* for the dot */
+    le = dn + *dn + 1;
+    ++dn;
+    do {
+      switch(*dn) {
+      case '.':
+      case '\\':
+      /* Special modifiers in zone files. */
+      case '"':
+      case ';':
+      case '@':
+      case '$':
+        size += 2;
+        break;
+      default:
+        if (*dn <= 0x20 || *dn >= 0x7f)
+          /* \ddd decimal notation */
+          size += 4;
+        else
+          size += 1;
+      }
+    } while(++dn < le);
+  }
+  size += 1;   /* zero byte at the end - string terminator */
+  return size > DNS_MAXNAME ? 0 : size;
+}
+
+/* Convert the dn into asciiz string.
+ * Keep in sync with dns_dntop_size() above.
+ */
+int dns_dntop(dnscc_t *dn, char *name, unsigned namesiz) {
+  char *np = name;                     /* current name ptr */
+  char *const ne = name + namesiz;     /* end of name */
+  dnscc_t *le;         /* label end */
+
+  while(*dn) {
+    /* *dn is the length of the next label, non-zero */
+    if (np != name) {
+      if (np >= ne) goto toolong;
+      *np++ = '.';
+    }
+    le = dn + *dn + 1;
+    ++dn;
+    do {
+      switch(*dn) {
+      case '.':
+      case '\\':
+      /* Special modifiers in zone files. */
+      case '"':
+      case ';':
+      case '@':
+      case '$':
+        if (np + 2 > ne) goto toolong;
+        *np++ = '\\';
+        *np++ = *dn;
+        break;
+      default:
+        if (*dn <= 0x20 || *dn >= 0x7f) {
+          /* \ddd decimal notation */
+          if (np + 4 >= ne) goto toolong;
+          *np++ = '\\';
+          *np++ = '0' + (*dn / 100);
+          *np++ = '0' + ((*dn % 100) / 10);
+          *np++ = '0' + (*dn % 10);
+        }
+        else {
+          if (np >= ne) goto toolong;
+          *np++ = *dn;
+        }
+      }
+    } while(++dn < le);
+  }
+  if (np >= ne) goto toolong;
+  *np++ = '\0';
+  return np - name;
+toolong:
+  return namesiz >= DNS_MAXNAME ? -1 : 0;
+}
+
+#ifdef TEST
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+  int i;
+  int sz;
+  dnsc_t dn[DNS_MAXDN+10];
+  dnsc_t *dl, *dp;
+  int isabs;
+
+  sz = (argc > 1) ? atoi(argv[1]) : 0;
+
+  for(i = 2; i < argc; ++i) {
+    int r = dns_ptodn(argv[i], 0, dn, sz, &isabs);
+    printf("%s: ", argv[i]);
+    if (r < 0) printf("error\n");
+    else if (!r) printf("buffer too small\n");
+    else {
+      printf("len=%d dnlen=%d size=%d name:",
+             r, dns_dnlen(dn), dns_dntop_size(dn));
+      dl = dn;
+      while(*dl) {
+        printf(" %d=", *dl);
+        dp = dl + 1;
+        dl = dp + *dl;
+        while(dp < dl) {
+          if (*dp <= ' ' || *dp >= 0x7f)
+            printf("\\%03d", *dp);
+          else if (*dp == '.' || *dp == '\\')
+            printf("\\%c", *dp);
+          else
+            putchar(*dp);
+          ++dp;
+        }
+      }
+      if (isabs) putchar('.');
+      putchar('\n');
+    }
+  }
+  return 0;
+}
+
+#endif /* TEST */
diff --git a/3rdparty/udns/udns_dntosp.c b/3rdparty/udns/udns_dntosp.c
new file mode 100644 (file)
index 0000000..823fde2
--- /dev/null
@@ -0,0 +1,30 @@
+/* udns_dntosp.c
+   dns_dntosp() = convert DN to asciiz string using static buffer
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include "udns.h"
+
+static char name[DNS_MAXNAME];
+
+const char *dns_dntosp(dnscc_t *dn) {
+  return dns_dntop(dn, name, sizeof(name)) > 0 ? name : 0;
+}
diff --git a/3rdparty/udns/udns_init.c b/3rdparty/udns/udns_init.c
new file mode 100644 (file)
index 0000000..493af58
--- /dev/null
@@ -0,0 +1,231 @@
+/* udns_init.c
+   resolver initialisation stuff
+
+   Copyright (C) 2006  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#ifdef WINDOWS
+# include <winsock2.h>          /* includes <windows.h> */
+# include <iphlpapi.h>         /* for dns server addresses etc */
+#else
+# include <sys/types.h>
+# include <unistd.h>
+# include <fcntl.h>
+#endif /* !WINDOWS */
+
+#include <stdlib.h>
+#include <string.h>
+#include "udns.h"
+
+#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n')
+
+static const char space[] = " \t\r\n";
+
+static void dns_set_serv_internal(struct dns_ctx *ctx, char *serv) {
+  dns_add_serv(ctx, NULL);
+  for(serv = strtok(serv, space); serv; serv = strtok(NULL, space))
+    dns_add_serv(ctx, serv);
+}
+
+static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) {
+  dns_add_srch(ctx, NULL);
+  for(srch = strtok(srch, space); srch; srch = strtok(NULL, space))
+    dns_add_srch(ctx, srch);
+}
+
+#ifdef WINDOWS
+
+#ifndef NO_IPHLPAPI
+/* Apparently, some systems does not have proper headers for IPHLPAIP to work.
+ * The best is to upgrade headers, but here's another, ugly workaround for
+ * this: compile with -DNO_IPHLPAPI.
+ */
+
+typedef DWORD (WINAPI *GetAdaptersAddressesFunc)(
+  ULONG Family, DWORD Flags, PVOID Reserved,
+  PIP_ADAPTER_ADDRESSES pAdapterAddresses,
+  PULONG pOutBufLen);
+
+static int dns_initns_iphlpapi(struct dns_ctx *ctx) {
+  HANDLE h_iphlpapi;
+  GetAdaptersAddressesFunc pfnGetAdAddrs;
+  PIP_ADAPTER_ADDRESSES pAddr, pAddrBuf;
+  PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsAddr;
+  ULONG ulOutBufLen;
+  DWORD dwRetVal;
+  int ret = -1;
+
+  h_iphlpapi = LoadLibrary("iphlpapi.dll");
+  if (!h_iphlpapi)
+    return -1;
+  pfnGetAdAddrs = (GetAdaptersAddressesFunc)
+    GetProcAddress(h_iphlpapi, "GetAdaptersAddresses");
+  if (!pfnGetAdAddrs) goto freelib;
+  ulOutBufLen = 0;
+  dwRetVal = pfnGetAdAddrs(AF_UNSPEC, 0, NULL, NULL, &ulOutBufLen);
+  if (dwRetVal != ERROR_BUFFER_OVERFLOW) goto freelib;
+  pAddrBuf = malloc(ulOutBufLen);
+  if (!pAddrBuf) goto freelib;
+  dwRetVal = pfnGetAdAddrs(AF_UNSPEC, 0, NULL, pAddrBuf, &ulOutBufLen);
+  if (dwRetVal != ERROR_SUCCESS) goto freemem;
+  for (pAddr = pAddrBuf; pAddr; pAddr = pAddr->Next)
+    for (pDnsAddr = pAddr->FirstDnsServerAddress;
+        pDnsAddr;
+        pDnsAddr = pDnsAddr->Next)
+      dns_add_serv_s(ctx, pDnsAddr->Address.lpSockaddr);
+  ret = 0;
+freemem:
+  free(pAddrBuf);
+freelib:
+  FreeLibrary(h_iphlpapi);
+  return ret;
+}
+
+#else /* NO_IPHLPAPI */
+
+#define dns_initns_iphlpapi(ctx) (-1)
+
+#endif /* NO_IPHLPAPI */
+
+static int dns_initns_registry(struct dns_ctx *ctx) {
+  LONG res;
+  HKEY hk;
+  DWORD type = REG_EXPAND_SZ | REG_SZ;
+  DWORD len;
+  char valBuf[1024];
+
+#define REGKEY_WINNT "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
+#define REGKEY_WIN9x "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP"
+  res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WINNT, 0, KEY_QUERY_VALUE, &hk);
+  if (res != ERROR_SUCCESS)
+    res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_WIN9x,
+                       0, KEY_QUERY_VALUE, &hk);
+  if (res != ERROR_SUCCESS)
+    return -1;
+  len = sizeof(valBuf) - 1;
+  res = RegQueryValueEx(hk, "NameServer", NULL, &type, (BYTE*)valBuf, &len);
+  if (res != ERROR_SUCCESS || !len || !valBuf[0]) {
+    len = sizeof(valBuf) - 1;
+    res = RegQueryValueEx(hk, "DhcpNameServer", NULL, &type,
+                          (BYTE*)valBuf, &len);
+  }
+  RegCloseKey(hk);
+  if (res != ERROR_SUCCESS || !len || !valBuf[0])
+    return -1;
+  valBuf[len] = '\0';
+  /* nameservers are stored as a whitespace-seperate list:
+   * "192.168.1.1 123.21.32.12" */
+  dns_set_serv_internal(ctx, valBuf);
+  return 0;
+}
+
+#else /* !WINDOWS */
+
+static int dns_init_resolvconf(struct dns_ctx *ctx) {
+  char *v;
+  char buf[2049];      /* this buffer is used to hold /etc/resolv.conf */
+  int has_srch = 0;
+
+  /* read resolv.conf... */
+  { int fd = open("/etc/resolv.conf", O_RDONLY);
+    if (fd >= 0) {
+      int l = read(fd, buf, sizeof(buf) - 1);
+      close(fd);
+      buf[l < 0 ? 0 : l] = '\0';
+    }
+    else
+      buf[0] = '\0';
+  }
+  if (buf[0]) {        /* ...and parse it */
+    char *line, *nextline;
+    line = buf;
+    do {
+      nextline = strchr(line, '\n');
+      if (nextline) *nextline++ = '\0';
+      v = line;
+      while(*v && !ISSPACE(*v)) ++v;
+      if (!*v) continue;
+      *v++ = '\0';
+      while(ISSPACE(*v)) ++v;
+      if (!*v) continue;
+      if (strcmp(line, "domain") == 0) {
+        dns_set_srch_internal(ctx, strtok(v, space));
+       has_srch = 1;
+      }
+      else if (strcmp(line, "search") == 0) {
+        dns_set_srch_internal(ctx, v);
+       has_srch = 1;
+      }
+      else if (strcmp(line, "nameserver") == 0)
+        dns_add_serv(ctx, strtok(v, space));
+      else if (strcmp(line, "options") == 0)
+        dns_set_opts(ctx, v);
+    } while((line = nextline) != NULL);
+  }
+
+  buf[sizeof(buf)-1] = '\0';
+
+  /* get list of nameservers from env. vars. */
+  if ((v = getenv("NSCACHEIP")) != NULL ||
+      (v = getenv("NAMESERVERS")) != NULL) {
+    strncpy(buf, v, sizeof(buf) - 1);
+    dns_set_serv_internal(ctx, buf);
+  }
+  /* if $LOCALDOMAIN is set, use it for search list */
+  if ((v = getenv("LOCALDOMAIN")) != NULL) {
+    strncpy(buf, v, sizeof(buf) - 1);
+    dns_set_srch_internal(ctx, buf);
+    has_srch = 1;
+  }
+  if ((v = getenv("RES_OPTIONS")) != NULL)
+    dns_set_opts(ctx, v);
+
+  /* if still no search list, use local domain name */
+  if (has_srch &&
+      gethostname(buf, sizeof(buf) - 1) == 0 &&
+      (v = strchr(buf, '.')) != NULL &&
+      *++v != '\0')
+    dns_add_srch(ctx, v);
+
+  return 0;
+}
+
+#endif /* !WINDOWS */
+
+int dns_init(struct dns_ctx *ctx, int do_open) {
+  if (!ctx)
+    ctx = &dns_defctx;
+  dns_reset(ctx);
+
+#ifdef WINDOWS
+  if (dns_initns_iphlpapi(ctx) != 0)
+    dns_initns_registry(ctx);
+  /*XXX WINDOWS: probably good to get default domain and search list too...
+   * And options.  Something is in registry. */
+  /*XXX WINDOWS: maybe environment variables are also useful? */
+#else
+  dns_init_resolvconf(ctx);
+#endif
+
+  return do_open ? dns_open(ctx) : 0;
+}
diff --git a/3rdparty/udns/udns_jran.c b/3rdparty/udns/udns_jran.c
new file mode 100644 (file)
index 0000000..19f9d02
--- /dev/null
@@ -0,0 +1,52 @@
+/* udns_jran.c: small non-cryptographic random number generator
+ * taken from http://burtleburtle.net/bob/rand/smallprng.html
+ * by Bob Jenkins, Public domain.
+ */
+
+#include "udns.h"
+
+#define rot32(x,k) (((x) << (k)) | ((x) >> (32-(k))))
+#define rot64(x,k) (((x) << (k)) | ((x) >> (64-(k))))
+#define tr32(x) ((x)&0xffffffffu)
+
+unsigned udns_jranval(struct udns_jranctx *x) {
+  /* This routine can be made to work with either 32 or 64bit words -
+   * if JRAN_32_64 is defined when compiling the file.
+   * We use if() instead of #if since there's no good
+   * portable way to check sizeof() in preprocessor without
+   * introducing some ugly configure-time checks.
+   * Most compilers will optimize the wrong branches away anyway.
+   * By default it assumes 32bit integers
+   */
+#ifdef JRAN_32_64
+  if (sizeof(unsigned) == 4) {
+#endif
+    unsigned e = tr32(x->a - rot32(x->b, 27));
+    x->a = tr32(x->b ^ rot32(x->c, 17));
+    x->b = tr32(x->c + x->d);
+    x->c = tr32(x->d + e);
+    x->d = tr32(e + x->a);
+#ifdef JRAN_32_64
+  }
+  else if (sizeof(unsigned) == 8) { /* assuming it's 64bits */
+    unsigned e = x->a - rot64(x->b, 7);
+    x->a = x->b ^ rot64(x->c, 13);
+    x->b = x->c + rot64(x->d, 37);
+    x->c = x->d + e;
+    x->d = e + x->a;
+  }
+  else {
+    unsigned e = 0;
+    x->d = 1/e; /* bail */
+  }
+#endif
+  return x->d;
+}
+
+void udns_jraninit(struct udns_jranctx *x, unsigned seed) {
+  unsigned i;
+  x->a = 0xf1ea5eed;
+  x->b = x->c = x->d = seed;
+  for (i = 0; i < 20; ++i)
+     (void)udns_jranval(x);
+}
diff --git a/3rdparty/udns/udns_misc.c b/3rdparty/udns/udns_misc.c
new file mode 100644 (file)
index 0000000..c162e70
--- /dev/null
@@ -0,0 +1,67 @@
+/* udns_misc.c
+   miscellaneous routines
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include "udns.h"
+
+int dns_findname(const struct dns_nameval *nv, const char *name) {
+  register const char *a, *b;
+  for(; nv->name; ++nv)
+    for(a = name, b = nv->name; ; ++a, ++b)
+      if (DNS_DNUC(*a) != *b) break;
+      else if (!*a) return nv->val;
+  return -1;
+}
+
+const char *_dns_format_code(char *buf, const char *prefix, int code) {
+  char *bp = buf;
+  unsigned c, n;
+  do *bp++ = DNS_DNUC(*prefix);
+  while(*++prefix);
+  *bp++ = '#';
+  if (code < 0) code = -code, *bp++ = '-';
+  n = 0; c = code;
+  do ++n;
+  while((c /= 10));
+  c = code;
+  bp[n--] = '\0';
+  do bp[n--] = c % 10 + '0';
+  while((c /= 10));
+  return buf;
+}
+
+const char *dns_strerror(int err) {
+  if (err >= 0) return "successeful completion";
+  switch(err) {
+  case DNS_E_TEMPFAIL: return "temporary failure in name resolution";
+  case DNS_E_PROTOCOL: return "protocol error";
+  case DNS_E_NXDOMAIN: return "domain name does not exist";
+  case DNS_E_NODATA:   return "valid domain but no data of requested type";
+  case DNS_E_NOMEM:    return "out of memory";
+  case DNS_E_BADQUERY: return "malformed query";
+  default:             return "unknown error";
+  }
+}
+
+const char *dns_version(void) {
+  return UDNS_VERSION;
+}
diff --git a/3rdparty/udns/udns_parse.c b/3rdparty/udns/udns_parse.c
new file mode 100644 (file)
index 0000000..8924b15
--- /dev/null
@@ -0,0 +1,169 @@
+/* udns_parse.c
+   raw DNS packet parsing routines
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include <string.h>
+#include <assert.h>
+#include "udns.h"
+
+dnscc_t *dns_skipdn(dnscc_t *cur, dnscc_t *end) {
+  unsigned c;
+  for(;;) {
+    if (cur >= end)
+      return NULL;
+    c = *cur++;
+    if (!c)
+      return cur;
+    if (c & 192)               /* jump */
+      return cur + 1 >= end ? NULL : cur + 1;
+    cur += c;
+  }
+}
+
+int
+dns_getdn(dnscc_t *pkt, dnscc_t **cur, dnscc_t *end,
+          register dnsc_t *dn, unsigned dnsiz) {
+  unsigned c;
+  dnscc_t *pp = *cur;          /* current packet pointer */
+  dnsc_t *dp = dn;             /* current dn pointer */
+  dnsc_t *const de             /* end of the DN dest */
+       = dn + (dnsiz < DNS_MAXDN ? dnsiz : DNS_MAXDN);
+  dnscc_t *jump = NULL;                /* ptr after first jump if any */
+  unsigned loop = 100;         /* jump loop counter */
+
+  for(;;) {            /* loop by labels */
+    if (pp >= end)             /* reached end of packet? */
+      return -1;
+    c = *pp++;                 /* length of the label */
+    if (!c) {                  /* empty label: terminate */
+      if (dn >= de)            /* can't fit terminator */
+        goto noroom;
+      *dp++ = 0;
+      /* return next pos: either after the first jump or current */
+      *cur = jump ? jump : pp;
+      return dp - dn;
+    }
+    if (c & 192) {             /* jump */
+      if (pp >= end)           /* eop instead of jump pos */
+        return -1;
+      if (!jump) jump = pp + 1;        /* remember first jump */
+      else if (!--loop) return -1; /* too many jumps */
+      c = ((c & ~192) << 8) | *pp; /* new pos */
+      if (c < DNS_HSIZE)       /* don't allow jump into the header */
+        return -1;
+      pp = pkt + c;
+      continue;
+    }
+    if (c > DNS_MAXLABEL)      /* too long label? */
+      return -1;
+    if (pp + c > end)          /* label does not fit in packet? */
+      return -1;
+    if (dp + c + 1 > de)       /* if enouth room for the label */
+      goto noroom;
+    *dp++ = c;                 /* label length */
+    memcpy(dp, pp, c);         /* and the label itself */
+    dp += c;
+    pp += c;                   /* advance to the next label */
+  }
+noroom:
+  return dnsiz < DNS_MAXDN ? 0 : -1;
+}
+
+void dns_rewind(struct dns_parse *p, dnscc_t *qdn) {
+  p->dnsp_qdn = qdn;
+  p->dnsp_cur = p->dnsp_ans;
+  p->dnsp_rrl = dns_numan(p->dnsp_pkt);
+  p->dnsp_ttl = 0xffffffffu;
+  p->dnsp_nrr = 0;
+}
+
+void
+dns_initparse(struct dns_parse *p, dnscc_t *qdn,
+              dnscc_t *pkt, dnscc_t *cur, dnscc_t *end) {
+  p->dnsp_pkt = pkt;
+  p->dnsp_end = end;
+  p->dnsp_rrl = dns_numan(pkt);
+  p->dnsp_qdn = qdn;
+  assert(cur + 4 <= end);
+  if ((p->dnsp_qtyp = dns_get16(cur+0)) == DNS_T_ANY) p->dnsp_qtyp = 0;
+  if ((p->dnsp_qcls = dns_get16(cur+2)) == DNS_C_ANY) p->dnsp_qcls = 0;
+  p->dnsp_cur = p->dnsp_ans = cur + 4;
+  p->dnsp_ttl = 0xffffffffu;
+  p->dnsp_nrr = 0;
+}
+
+int dns_nextrr(struct dns_parse *p, struct dns_rr *rr) {
+  dnscc_t *cur = p->dnsp_cur;
+  while(p->dnsp_rrl > 0) {
+    --p->dnsp_rrl;
+    if (dns_getdn(p->dnsp_pkt, &cur, p->dnsp_end,
+                  rr->dnsrr_dn, sizeof(rr->dnsrr_dn)) <= 0)
+      return -1;
+    if (cur + 10 > p->dnsp_end)
+      return -1;
+    rr->dnsrr_typ = dns_get16(cur);
+    rr->dnsrr_cls = dns_get16(cur+2);
+    rr->dnsrr_ttl = dns_get32(cur+4);
+    rr->dnsrr_dsz = dns_get16(cur+8);
+    rr->dnsrr_dptr = cur = cur + 10;
+    rr->dnsrr_dend = cur = cur + rr->dnsrr_dsz;
+    if (cur > p->dnsp_end)
+      return -1;
+    if (p->dnsp_qdn && !dns_dnequal(p->dnsp_qdn, rr->dnsrr_dn))
+      continue;
+    if ((!p->dnsp_qcls || p->dnsp_qcls == rr->dnsrr_cls) &&
+        (!p->dnsp_qtyp || p->dnsp_qtyp == rr->dnsrr_typ)) {
+      p->dnsp_cur = cur;
+      ++p->dnsp_nrr;
+      if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl;
+      return 1;
+    }
+    if (p->dnsp_qdn && rr->dnsrr_typ == DNS_T_CNAME && !p->dnsp_nrr) {
+      if (dns_getdn(p->dnsp_pkt, &rr->dnsrr_dptr, p->dnsp_end,
+                    p->dnsp_dnbuf, sizeof(p->dnsp_dnbuf)) <= 0 ||
+          rr->dnsrr_dptr != rr->dnsrr_dend)
+        return -1;
+      p->dnsp_qdn = p->dnsp_dnbuf;
+      if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl;
+    }
+  }
+  p->dnsp_cur = cur;
+  return 0;
+}
+
+int dns_stdrr_size(const struct dns_parse *p) {
+  return
+    dns_dntop_size(p->dnsp_qdn) +
+    (p->dnsp_qdn == dns_payload(p->dnsp_pkt) ? 0 :
+     dns_dntop_size(dns_payload(p->dnsp_pkt)));
+}
+
+void *dns_stdrr_finish(struct dns_rr_null *ret, char *cp,
+                       const struct dns_parse *p) {
+  cp += dns_dntop(p->dnsp_qdn, (ret->dnsn_cname = cp), DNS_MAXNAME);
+  if (p->dnsp_qdn == dns_payload(p->dnsp_pkt))
+    ret->dnsn_qname = ret->dnsn_cname;
+  else
+    dns_dntop(dns_payload(p->dnsp_pkt), (ret->dnsn_qname = cp), DNS_MAXNAME);
+  ret->dnsn_ttl = p->dnsp_ttl;
+  return ret;
+}
diff --git a/3rdparty/udns/udns_resolver.c b/3rdparty/udns/udns_resolver.c
new file mode 100644 (file)
index 0000000..b8f899a
--- /dev/null
@@ -0,0 +1,1323 @@
+/* udns_resolver.c
+   resolver stuff (main module)
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#ifdef WINDOWS
+# include <winsock2.h>          /* includes <windows.h> */
+# include <ws2tcpip.h>          /* needed for struct in6_addr */
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <unistd.h>
+# include <fcntl.h>
+# include <sys/time.h>
+# ifdef HAVE_POLL
+#  include <sys/poll.h>
+# else
+#  ifdef HAVE_SYS_SELECT_H
+#   include <sys/select.h>
+#  endif
+# endif
+# ifdef HAVE_TIMES
+#  include <sys/times.h>
+# endif
+# define closesocket(sock) close(sock)
+#endif /* !WINDOWS */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <assert.h>
+#include <stddef.h>
+#include "udns.h"
+
+#ifndef EAFNOSUPPORT
+# define EAFNOSUPPORT EINVAL
+#endif
+#ifndef MSG_DONTWAIT
+# define MSG_DONTWAIT 0
+#endif
+
+struct dns_qlist {
+  struct dns_query *head, *tail;
+};
+
+struct dns_query {
+  struct dns_query *dnsq_next;          /* double-linked list */
+  struct dns_query *dnsq_prev;
+  unsigned dnsq_origdnl0;              /* original query DN len w/o last 0 */
+  unsigned dnsq_flags;                 /* control flags for this query */
+  unsigned dnsq_servi;                 /* index of next server to try */
+  unsigned dnsq_servwait;              /* bitmask: servers left to wait */
+  unsigned dnsq_servskip;              /* bitmask: servers to skip */
+  unsigned dnsq_servnEDNS0;            /* bitmask: servers refusing EDNS0 */
+  unsigned dnsq_try;                   /* number of tries made so far */
+  dnscc_t *dnsq_nxtsrch;               /* next search pointer @dnsc_srchbuf */
+  time_t dnsq_deadline;                        /* when current try will expire */
+  dns_parse_fn *dnsq_parse;            /* parse: raw => application */
+  dns_query_fn *dnsq_cbck;             /* the callback to call when done */
+  void *dnsq_cbdata;                   /* user data for the callback */
+#ifndef NDEBUG
+  struct dns_ctx *dnsq_ctx;            /* the resolver context */
+#endif
+  /* char fields at the end to avoid padding */
+  dnsc_t dnsq_id[2];                   /* query ID */
+  dnsc_t dnsq_typcls[4];               /* requested RR type+class */
+  dnsc_t dnsq_dn[DNS_MAXDN+DNS_DNPAD]; /* the query DN +alignment */
+};
+
+/* working with dns_query lists */
+
+static __inline void qlist_init(struct dns_qlist *list) {
+  list->head = list->tail = NULL;
+}
+
+static __inline void qlist_remove(struct dns_qlist *list, struct dns_query *q) {
+   if (q->dnsq_prev) q->dnsq_prev->dnsq_next = q->dnsq_next;
+   else list->head = q->dnsq_next;
+   if (q->dnsq_next) q->dnsq_next->dnsq_prev = q->dnsq_prev;
+   else list->tail = q->dnsq_prev;
+}
+
+static __inline void
+qlist_add_head(struct dns_qlist *list, struct dns_query *q) {
+  q->dnsq_next = list->head;
+  if (list->head) list->head->dnsq_prev = q;
+  else list->tail = q;
+  list->head = q;
+  q->dnsq_prev = NULL;
+}
+
+static __inline void
+qlist_insert_after(struct dns_qlist *list,
+                   struct dns_query *q, struct dns_query *prev) {
+  if ((q->dnsq_prev = prev) != NULL) {
+    if ((q->dnsq_next = prev->dnsq_next) != NULL)
+      q->dnsq_next->dnsq_prev = q;
+    else
+      list->tail = q;
+    prev->dnsq_next = q;
+  }
+  else
+    qlist_add_head(list, q);
+}
+
+union sockaddr_ns {
+  struct sockaddr sa;
+  struct sockaddr_in sin;
+#ifdef HAVE_IPv6
+  struct sockaddr_in6 sin6;
+#endif
+};
+
+#define sin_eq(a,b) \
+       ((a).sin_port == (b).sin_port && \
+        (a).sin_addr.s_addr == (b).sin_addr.s_addr)
+#define sin6_eq(a,b) \
+       ((a).sin6_port == (b).sin6_port && \
+        memcmp(&(a).sin6_addr, &(b).sin6_addr, sizeof(struct in6_addr)) == 0)
+
+struct dns_ctx {               /* resolver context */
+  /* settings */
+  unsigned dnsc_flags;                 /* various flags */
+  unsigned dnsc_timeout;               /* timeout (base value) for queries */
+  unsigned dnsc_ntries;                        /* number of retries */
+  unsigned dnsc_ndots;                 /* ndots to assume absolute name */
+  unsigned dnsc_port;                  /* default port (DNS_PORT) */
+  unsigned dnsc_udpbuf;                        /* size of UDP buffer */
+  /* array of nameserver addresses */
+  union sockaddr_ns dnsc_serv[DNS_MAXSERV];
+  unsigned dnsc_nserv;                 /* number of nameservers */
+  unsigned dnsc_salen;                 /* length of socket addresses */
+  dnsc_t dnsc_srchbuf[1024];           /* buffer for searchlist */
+  dnsc_t *dnsc_srchend;                        /* current end of srchbuf */
+
+  dns_utm_fn *dnsc_utmfn;              /* register/cancel timer events */
+  void *dnsc_utmctx;                   /* user timer context for utmfn() */
+  time_t dnsc_utmexp;                  /* when user timer expires */
+
+  dns_dbgfn *dnsc_udbgfn;              /* debugging function */
+
+  /* dynamic data */
+  struct udns_jranctx dnsc_jran;       /* random number generator state */
+  unsigned dnsc_nextid;                        /* next queue ID to use if !0 */
+  int dnsc_udpsock;                    /* UDP socket */
+  struct dns_qlist dnsc_qactive;       /* active list sorted by deadline */
+  int dnsc_nactive;                    /* number entries in dnsc_qactive */
+  dnsc_t *dnsc_pbuf;                   /* packet buffer (udpbuf size) */
+  int dnsc_qstatus;                    /* last query status value */
+};
+
+static const struct {
+  const char *name;
+  enum dns_opt opt;
+  unsigned offset;
+  unsigned min, max;
+} dns_opts[] = {
+#define opt(name,opt,field,min,max) \
+       {name,opt,offsetof(struct dns_ctx,field),min,max}
+  opt("retrans", DNS_OPT_TIMEOUT, dnsc_timeout, 1,300),
+  opt("timeout", DNS_OPT_TIMEOUT, dnsc_timeout, 1,300),
+  opt("retry",    DNS_OPT_NTRIES, dnsc_ntries, 1,50),
+  opt("attempts", DNS_OPT_NTRIES, dnsc_ntries, 1,50),
+  opt("ndots", DNS_OPT_NDOTS, dnsc_ndots, 0,1000),
+  opt("port", DNS_OPT_PORT, dnsc_port, 1,0xffff),
+  opt("udpbuf", DNS_OPT_UDPSIZE, dnsc_udpbuf, DNS_MAXPACKET,65536),
+#undef opt
+};
+#define dns_ctxopt(ctx,idx) (*((unsigned*)(((char*)ctx)+dns_opts[idx].offset)))
+
+#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n')
+
+struct dns_ctx dns_defctx;
+
+#define SETCTX(ctx) if (!ctx) ctx = &dns_defctx
+#define SETCTXINITED(ctx) SETCTX(ctx); assert(CTXINITED(ctx))
+#define CTXINITED(ctx) (ctx->dnsc_flags & DNS_INITED)
+#define SETCTXFRESH(ctx) SETCTXINITED(ctx); assert(!CTXOPEN(ctx))
+#define SETCTXINACTIVE(ctx) \
+               SETCTXINITED(ctx); assert(!ctx->dnsc_nactive)
+#define SETCTXOPEN(ctx) SETCTXINITED(ctx); assert(CTXOPEN(ctx))
+#define CTXOPEN(ctx) (ctx->dnsc_udpsock >= 0)
+
+#if defined(NDEBUG) || !defined(DEBUG)
+#define dns_assert_ctx(ctx)
+#else
+static void dns_assert_ctx(const struct dns_ctx *ctx) {
+  int nactive = 0;
+  const struct dns_query *q;
+  for(q = ctx->dnsc_qactive.head; q; q = q->dnsq_next) {
+    assert(q->dnsq_ctx == ctx);
+    assert(q == (q->dnsq_next ?
+                 q->dnsq_next->dnsq_prev : ctx->dnsc_qactive.tail));
+    assert(q == (q->dnsq_prev ?
+                 q->dnsq_prev->dnsq_next : ctx->dnsc_qactive.head));
+    ++nactive;
+  }
+  assert(nactive == ctx->dnsc_nactive);
+}
+#endif
+
+enum {
+  DNS_INTERNAL         = 0xffff, /* internal flags mask */
+  DNS_INITED           = 0x0001, /* the context is initialized */
+  DNS_ASIS_DONE                = 0x0002, /* search: skip the last as-is query */
+  DNS_SEEN_NODATA      = 0x0004, /* search: NODATA has been received */
+};
+
+int dns_add_serv(struct dns_ctx *ctx, const char *serv) {
+  union sockaddr_ns *sns;
+  SETCTXFRESH(ctx);
+  if (!serv)
+    return (ctx->dnsc_nserv = 0);
+  if (ctx->dnsc_nserv >= DNS_MAXSERV)
+    return errno = ENFILE, -1;
+  sns = &ctx->dnsc_serv[ctx->dnsc_nserv];
+  memset(sns, 0, sizeof(*sns));
+  if (dns_pton(AF_INET, serv, &sns->sin.sin_addr) > 0) {
+    sns->sin.sin_family = AF_INET;
+    return ++ctx->dnsc_nserv;
+  }
+#ifdef HAVE_IPv6
+  if (dns_pton(AF_INET6, serv, &sns->sin6.sin6_addr) > 0) {
+    sns->sin6.sin6_family = AF_INET6;
+    return ++ctx->dnsc_nserv;
+  }
+#endif
+  errno = EINVAL;
+  return -1;
+}
+
+int dns_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa) {
+  SETCTXFRESH(ctx);
+  if (!sa)
+    return (ctx->dnsc_nserv = 0);
+  if (ctx->dnsc_nserv >= DNS_MAXSERV)
+    return errno = ENFILE, -1;
+#ifdef HAVE_IPv6
+  else if (sa->sa_family == AF_INET6)
+    ctx->dnsc_serv[ctx->dnsc_nserv].sin6 = *(struct sockaddr_in6*)sa;
+#endif
+  else if (sa->sa_family == AF_INET)
+    ctx->dnsc_serv[ctx->dnsc_nserv].sin = *(struct sockaddr_in*)sa;
+  else
+    return errno = EAFNOSUPPORT, -1;
+  return ++ctx->dnsc_nserv;
+}
+
+int dns_set_opts(struct dns_ctx *ctx, const char *opts) {
+  unsigned i, v;
+  int err = 0;
+  SETCTXINACTIVE(ctx);
+  for(;;) {
+    while(ISSPACE(*opts)) ++opts;
+    if (!*opts) break;
+    for(i = 0; ; ++i) {
+      if (i >= sizeof(dns_opts)/sizeof(dns_opts[0])) { ++err; break; }
+      v = strlen(dns_opts[i].name);
+      if (strncmp(dns_opts[i].name, opts, v) != 0 ||
+          (opts[v] != ':' && opts[v] != '='))
+        continue;
+      opts += v + 1;
+      v = 0;
+      if (*opts < '0' || *opts > '9') { ++err; break; }
+      do v = v * 10 + (*opts++ - '0');
+      while (*opts >= '0' && *opts <= '9');
+      if (v < dns_opts[i].min) v = dns_opts[i].min;
+      if (v > dns_opts[i].max) v = dns_opts[i].max;
+      dns_ctxopt(ctx, i) = v;
+      break;
+    }
+    while(*opts && !ISSPACE(*opts)) ++opts;
+  }
+  return err;
+}
+
+int dns_set_opt(struct dns_ctx *ctx, enum dns_opt opt, int val) {
+  int prev;
+  unsigned i;
+  SETCTXINACTIVE(ctx);
+  for(i = 0; i < sizeof(dns_opts)/sizeof(dns_opts[0]); ++i) {
+    if (dns_opts[i].opt != opt) continue;
+    prev = dns_ctxopt(ctx, i);
+    if (val >= 0) {
+      unsigned v = val;
+      if (v < dns_opts[i].min || v > dns_opts[i].max) {
+        errno = EINVAL;
+        return -1;
+      }
+      dns_ctxopt(ctx, i) = v;
+    }
+    return prev;
+  }
+  if (opt == DNS_OPT_FLAGS) {
+    prev = ctx->dnsc_flags & ~DNS_INTERNAL;
+    if (val >= 0)
+      ctx->dnsc_flags =
+        (ctx->dnsc_flags & DNS_INTERNAL) | (val & ~DNS_INTERNAL);
+    return prev;
+  }
+  errno = ENOSYS;
+  return -1;
+}
+
+int dns_add_srch(struct dns_ctx *ctx, const char *srch) {
+  int dnl;
+  SETCTXINACTIVE(ctx);
+  if (!srch) {
+    memset(ctx->dnsc_srchbuf, 0, sizeof(ctx->dnsc_srchbuf));
+    ctx->dnsc_srchend = ctx->dnsc_srchbuf;
+    return 0;
+  }
+  dnl =
+    sizeof(ctx->dnsc_srchbuf) - (ctx->dnsc_srchend - ctx->dnsc_srchbuf) - 1;
+  dnl = dns_sptodn(srch, ctx->dnsc_srchend, dnl);
+  if (dnl > 0)
+    ctx->dnsc_srchend += dnl;
+  ctx->dnsc_srchend[0] = '\0'; /* we ensure the list is always ends at . */
+  if (dnl > 0)
+    return 0;
+  errno = EINVAL;
+  return -1;
+}
+
+static void dns_drop_utm(struct dns_ctx *ctx) {
+  if (ctx->dnsc_utmfn)
+    ctx->dnsc_utmfn(NULL, -1, ctx->dnsc_utmctx);
+  ctx->dnsc_utmctx = NULL;
+  ctx->dnsc_utmexp = -1;
+}
+
+static void
+_dns_request_utm(struct dns_ctx *ctx, time_t now) {
+  struct dns_query *q;
+  time_t deadline;
+  int timeout;
+  q = ctx->dnsc_qactive.head;
+  if (!q)
+    deadline = -1, timeout = -1;
+  else if (!now || q->dnsq_deadline <= now)
+    deadline = 0, timeout = 0;
+  else
+    deadline = q->dnsq_deadline, timeout = (int)(deadline - now);
+  if (ctx->dnsc_utmexp == deadline)
+    return;
+  ctx->dnsc_utmfn(ctx, timeout, ctx->dnsc_utmctx);
+  ctx->dnsc_utmexp = deadline;
+}
+
+static __inline void
+dns_request_utm(struct dns_ctx *ctx, time_t now) {
+  if (ctx->dnsc_utmfn)
+    _dns_request_utm(ctx, now);
+}
+
+void dns_set_dbgfn(struct dns_ctx *ctx, dns_dbgfn *dbgfn) {
+  SETCTXINITED(ctx);
+  ctx->dnsc_udbgfn = dbgfn;
+}
+
+void
+dns_set_tmcbck(struct dns_ctx *ctx, dns_utm_fn *fn, void *data) {
+  SETCTXINITED(ctx);
+  dns_drop_utm(ctx);
+  ctx->dnsc_utmfn = fn;
+  ctx->dnsc_utmctx = data;
+  if (CTXOPEN(ctx))
+    dns_request_utm(ctx, 0);
+}
+
+static unsigned dns_nonrandom_32(void) {
+#ifdef WINDOWS
+  FILETIME ft;
+  GetSystemTimeAsFileTime(&ft);
+  return ft.dwLowDateTime;
+#else
+  struct timeval tv;
+  gettimeofday(&tv, NULL);
+  return tv.tv_usec;
+#endif
+}
+
+/* This is historic deprecated API */
+UDNS_API unsigned dns_random16(void);
+unsigned dns_random16(void) {
+  unsigned x = dns_nonrandom_32();
+  return (x ^ (x >> 16)) & 0xffff;
+}
+
+static void dns_init_rng(struct dns_ctx *ctx) {
+  udns_jraninit(&ctx->dnsc_jran, dns_nonrandom_32());
+  ctx->dnsc_nextid = 0;
+}
+
+void dns_close(struct dns_ctx *ctx) {
+  struct dns_query *q, *p;
+  SETCTX(ctx);
+  if (CTXINITED(ctx)) {
+    if (ctx->dnsc_udpsock >= 0)
+      closesocket(ctx->dnsc_udpsock);
+    ctx->dnsc_udpsock = -1;
+    if (ctx->dnsc_pbuf)
+      free(ctx->dnsc_pbuf);
+    ctx->dnsc_pbuf = NULL;
+    q = ctx->dnsc_qactive.head;
+    while((p = q) != NULL) {
+      q = q->dnsq_next;
+      free(p);
+    }
+    qlist_init(&ctx->dnsc_qactive);
+    ctx->dnsc_nactive = 0;
+    dns_drop_utm(ctx);
+  }
+}
+
+void dns_reset(struct dns_ctx *ctx) {
+  SETCTX(ctx);
+  dns_close(ctx);
+  memset(ctx, 0, sizeof(*ctx));
+  ctx->dnsc_timeout = 4;
+  ctx->dnsc_ntries = 3;
+  ctx->dnsc_ndots = 1;
+  ctx->dnsc_udpbuf = DNS_EDNS0PACKET;
+  ctx->dnsc_port = DNS_PORT;
+  ctx->dnsc_udpsock = -1;
+  ctx->dnsc_srchend = ctx->dnsc_srchbuf;
+  qlist_init(&ctx->dnsc_qactive);
+  dns_init_rng(ctx);
+  ctx->dnsc_flags = DNS_INITED;
+}
+
+struct dns_ctx *dns_new(const struct dns_ctx *copy) {
+  struct dns_ctx *ctx;
+  SETCTXINITED(copy);
+  dns_assert_ctx(copy);
+  ctx = malloc(sizeof(*ctx));
+  if (!ctx)
+    return NULL;
+  *ctx = *copy;
+  ctx->dnsc_udpsock = -1;
+  qlist_init(&ctx->dnsc_qactive);
+  ctx->dnsc_nactive = 0;
+  ctx->dnsc_pbuf = NULL;
+  ctx->dnsc_qstatus = 0;
+  ctx->dnsc_srchend = ctx->dnsc_srchbuf +
+    (copy->dnsc_srchend - copy->dnsc_srchbuf);
+  ctx->dnsc_utmfn = NULL;
+  ctx->dnsc_utmctx = NULL;
+  dns_init_rng(ctx);
+  return ctx;
+}
+
+void dns_free(struct dns_ctx *ctx) {
+  assert(ctx != NULL && ctx != &dns_defctx);
+  dns_reset(ctx);
+  free(ctx);
+}
+
+int dns_open(struct dns_ctx *ctx) {
+  int sock;
+  unsigned i;
+  int port;
+  union sockaddr_ns *sns;
+#ifdef HAVE_IPv6
+  unsigned have_inet6 = 0;
+#endif
+
+  SETCTXINITED(ctx);
+  assert(!CTXOPEN(ctx));
+
+  port = htons((unsigned short)ctx->dnsc_port);
+  /* ensure we have at least one server */
+  if (!ctx->dnsc_nserv) {
+    sns = ctx->dnsc_serv;
+    sns->sin.sin_family = AF_INET;
+    sns->sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    ctx->dnsc_nserv = 1;
+  }
+
+  for (i = 0; i < ctx->dnsc_nserv; ++i) {
+    sns = &ctx->dnsc_serv[i];
+    /* set port for each sockaddr */
+#ifdef HAVE_IPv6
+    if (sns->sa.sa_family == AF_INET6) {
+      if (!sns->sin6.sin6_port) sns->sin6.sin6_port = (unsigned short)port;
+      ++have_inet6;
+    }
+    else
+#endif
+    {
+      assert(sns->sa.sa_family == AF_INET);
+      if (!sns->sin.sin_port) sns->sin.sin_port = (unsigned short)port;
+    }
+  }
+
+#ifdef HAVE_IPv6
+  if (have_inet6 && have_inet6 < ctx->dnsc_nserv) {
+    /* convert all IPv4 addresses to IPv6 V4MAPPED */
+    struct sockaddr_in6 sin6;
+    memset(&sin6, 0, sizeof(sin6));
+    sin6.sin6_family = AF_INET6;
+    /* V4MAPPED: ::ffff:1.2.3.4 */
+    sin6.sin6_addr.s6_addr[10] = 0xff;
+    sin6.sin6_addr.s6_addr[11] = 0xff;
+    for(i = 0; i < ctx->dnsc_nserv; ++i) {
+      sns = &ctx->dnsc_serv[i];
+      if (sns->sa.sa_family == AF_INET) {
+        sin6.sin6_port = sns->sin.sin_port;
+        memcpy(sin6.sin6_addr.s6_addr + 4*3, &sns->sin.sin_addr, 4);
+        sns->sin6 = sin6;
+      }
+    }
+  }
+
+  ctx->dnsc_salen = have_inet6 ?
+    sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
+
+  if (have_inet6)
+    sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+  else
+    sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+#else /* !HAVE_IPv6 */
+  sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  ctx->dnsc_salen = sizeof(struct sockaddr_in);
+#endif /* HAVE_IPv6 */
+
+  if (sock < 0) {
+    ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
+    return -1;
+  }
+#ifdef WINDOWS
+  { unsigned long on = 1;
+    if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) {
+      closesocket(sock);
+      ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
+      return -1;
+    }
+  }
+#else  /* !WINDOWS */
+  if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK) < 0 ||
+      fcntl(sock, F_SETFD, FD_CLOEXEC) < 0) {
+    closesocket(sock);
+    ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
+    return -1;
+  }
+#endif /* WINDOWS */
+  /* allocate the packet buffer */
+  if ((ctx->dnsc_pbuf = malloc(ctx->dnsc_udpbuf)) == NULL) {
+    closesocket(sock);
+    ctx->dnsc_qstatus = DNS_E_NOMEM;
+    errno = ENOMEM;
+    return -1;
+  }
+
+  ctx->dnsc_udpsock = sock;
+  dns_request_utm(ctx, 0);
+  return sock;
+}
+
+int dns_sock(const struct dns_ctx *ctx) {
+  SETCTXINITED(ctx);
+  return ctx->dnsc_udpsock;
+}
+
+int dns_active(const struct dns_ctx *ctx) {
+  SETCTXINITED(ctx);
+  dns_assert_ctx(ctx);
+  return ctx->dnsc_nactive;
+}
+
+int dns_status(const struct dns_ctx *ctx) {
+  SETCTX(ctx);
+  return ctx->dnsc_qstatus;
+}
+void dns_setstatus(struct dns_ctx *ctx, int status) {
+  SETCTX(ctx);
+  ctx->dnsc_qstatus = status;
+}
+
+/* End the query: disconnect it from the active list, free it,
+ * and return the result to the caller.
+ */
+static void
+dns_end_query(struct dns_ctx *ctx, struct dns_query *q,
+              int status, void *result) {
+  dns_query_fn *cbck = q->dnsq_cbck;
+  void *cbdata = q->dnsq_cbdata;
+  ctx->dnsc_qstatus = status;
+  assert((status < 0 && result == 0) || (status >= 0 && result != 0));
+  assert(cbck != 0);   /*XXX callback may be NULL */
+  assert(ctx->dnsc_nactive > 0);
+  --ctx->dnsc_nactive;
+  qlist_remove(&ctx->dnsc_qactive, q);
+  /* force the query to be unconnected */
+  /*memset(q, 0, sizeof(*q));*/
+#ifndef NDEBUG
+  q->dnsq_ctx = NULL;
+#endif
+  free(q);
+  cbck(ctx, result, cbdata);
+}
+
+#define DNS_DBG(ctx, code, sa, slen, pkt, plen) \
+  do { \
+    if (ctx->dnsc_udbgfn) \
+      ctx->dnsc_udbgfn(code, (sa), slen, pkt, plen, 0, 0); \
+  } while(0)
+#define DNS_DBGQ(ctx, q, code, sa, slen, pkt, plen) \
+  do { \
+    if (ctx->dnsc_udbgfn) \
+      ctx->dnsc_udbgfn(code, (sa), slen, pkt, plen, q, q->dnsq_cbdata); \
+  } while(0)
+
+static void dns_newid(struct dns_ctx *ctx, struct dns_query *q) {
+  /* this is how we choose an identifier for a new query (qID).
+   * For now, it's just sequential number, incremented for every query, and
+   * thus obviously trivial to guess.
+   * There are two choices:
+   *  a) use sequential numbers.  It is plain insecure. In DNS, there are two
+   *   places where random numbers are (or can) be used to increase security:
+   *   random qID and random source port number.  Without this randomness
+   *   (udns uses fixed port for all queries), or when the randomness is weak,
+   *   it's trivial to spoof query replies.  With randomness however, it
+   *   becomes a bit more difficult task.  Too bad we only have 16 bits for
+   *   our security, as qID is only two bytes.  It isn't a security per se,
+   *   to rely on those 16 bits - an attacker can just flood us with fake
+   *   replies with all possible qIDs (only 65536 of them), and in this case,
+   *   even if we'll use true random qIDs, we'll be in trouble (not protected
+   *   against spoofing).  Yes, this is only possible on a high-speed network
+   *   (probably on the LAN only, since usually a border router for a LAN
+   *   protects internal machines from packets with spoofed local addresses
+   *   from outside, and usually a nameserver resides on LAN), but it's
+   *   still very well possible to send us fake replies.
+   *   In other words: there's nothing a DNS (stub) resolver can do against
+   *   spoofing attacks, unless DNSSEC is in use, which helps here alot.
+   *   Too bad that DNSSEC isn't widespread, so relying on it isn't an
+   *   option in almost all cases...
+   *  b) use random qID, based on some random-number generation mechanism.
+   *   This way, we increase our protection a bit (see above - it's very weak
+   *   still), but we also increase risk of qID reuse and matching late replies
+   *   that comes to queries we've sent before against new queries.  There are
+   *   some more corner cases around that, as well - for example, normally,
+   *   udns tries to find the query for a given reply by qID, *and* by
+   *   verifying that the query DN and other parameters are also the same
+   *   (so if the new query is against another domain name, old reply will
+   *   be ignored automatically).  But certain types of replies which we now
+   *   handle - for example, FORMERR reply from servers which refuses to
+   *   process EDNS0-enabled packets - comes without all the query parameters
+   *   but the qID - so we're forced to use qID only when determining which
+   *   query the given reply corresponds to.  This makes us even more
+   *   vulnerable to spoofing attacks, because an attacker don't even need to
+   *   know which queries we perform to spoof the replies - he only needs to
+   *   flood us with fake FORMERR "replies".
+   *
+   * That all to say: using sequential (or any other trivially guessable)
+   * numbers for qIDs is insecure, but the whole thing is inherently insecure
+   * as well, and this "extra weakness" that comes from weak qID choosing
+   * algorithm adds almost nothing to the underlying problem.
+   *
+   * It CAN NOT be made secure.  Period.  That's it.
+   * Unless we choose to implement DNSSEC, which is a whole different story.
+   * Forcing TCP mode makes it better, but who uses TCP for DNS anyway?
+   * (and it's hardly possible because of huge impact on the recursive
+   * nameservers).
+   *
+   * Note that ALL stub resolvers (again, unless they implement and enforce
+   * DNSSEC) suffers from this same problem.
+   *
+   * Here, I use a pseudo-random number generator for qIDs, instead of a
+   * simpler sequential IDs.  This is _not_ more secure than sequential
+   * ID, but some found random IDs more enjoyeable for some reason.  So
+   * here it goes.
+   */
+
+  /* Use random number and check if it's unique.
+   * If it's not, try again up to 5 times.
+   */
+  unsigned loop;
+  dnsc_t c0, c1;
+  for(loop = 0; loop < 5; ++loop) {
+    const struct dns_query *c;
+    if (!ctx->dnsc_nextid)
+      ctx->dnsc_nextid = udns_jranval(&ctx->dnsc_jran);
+    c0 = ctx->dnsc_nextid & 0xff;
+    c1 = (ctx->dnsc_nextid >> 8) & 0xff;
+    ctx->dnsc_nextid >>= 16;
+    for(c = ctx->dnsc_qactive.head; c; c = c->dnsq_next)
+      if (c->dnsq_id[0] == c0 && c->dnsq_id[1] == c1)
+        break; /* found such entry, try again */
+    if (!c)
+      break;
+  }
+  q->dnsq_id[0] = c0; q->dnsq_id[1] = c1;
+
+  /* reset all parameters relevant for previous query lifetime */
+  q->dnsq_try = 0;
+  q->dnsq_servi = 0;
+  /*XXX probably should keep dnsq_servnEDNS0 bits?
+   * See also comments in dns_ioevent() about FORMERR case */
+  q->dnsq_servwait = q->dnsq_servskip = q->dnsq_servnEDNS0 = 0;
+}
+
+/* Find next search suffix and fills in q->dnsq_dn.
+ * Return 0 if no more to try. */
+static int dns_next_srch(struct dns_ctx *ctx, struct dns_query *q) {
+  unsigned dnl;
+
+  for(;;) {
+    if (q->dnsq_nxtsrch > ctx->dnsc_srchend)
+      return 0;
+    dnl = dns_dnlen(q->dnsq_nxtsrch);
+    if (dnl + q->dnsq_origdnl0 <= DNS_MAXDN &&
+        (*q->dnsq_nxtsrch || !(q->dnsq_flags & DNS_ASIS_DONE)))
+      break;
+    q->dnsq_nxtsrch += dnl;
+  }
+  memcpy(q->dnsq_dn + q->dnsq_origdnl0, q->dnsq_nxtsrch, dnl);
+  if (!*q->dnsq_nxtsrch)
+    q->dnsq_flags |= DNS_ASIS_DONE;
+  q->dnsq_nxtsrch += dnl;
+  dns_newid(ctx, q); /* new ID for new qDN */
+  return 1;
+}
+
+/* find the server to try for current iteration.
+ * Note that current dnsq_servi may point to a server we should skip --
+ * in that case advance to the next server.
+ * Return true if found, false if all tried.
+ */
+static int dns_find_serv(const struct dns_ctx *ctx, struct dns_query *q) {
+  while(q->dnsq_servi < ctx->dnsc_nserv) {
+    if (!(q->dnsq_servskip & (1 << q->dnsq_servi)))
+      return 1;
+    ++q->dnsq_servi;
+  }
+  return 0;
+}
+
+/* format and send the query to a given server.
+ * In case of network problem (sendto() fails), return -1,
+ * else return 0.
+ */
+static int
+dns_send_this(struct dns_ctx *ctx, struct dns_query *q,
+              unsigned servi, time_t now) {
+  unsigned qlen;
+  unsigned tries;
+
+  { /* format the query buffer */
+    dnsc_t *p = ctx->dnsc_pbuf;
+    memset(p, 0, DNS_HSIZE);
+    if (!(q->dnsq_flags & DNS_NORD)) p[DNS_H_F1] |= DNS_HF1_RD;
+    if (q->dnsq_flags & DNS_AAONLY) p[DNS_H_F1] |= DNS_HF1_AA;
+    if (q->dnsq_flags & DNS_SET_CD) p[DNS_H_F2] |= DNS_HF2_CD;
+    p[DNS_H_QDCNT2] = 1;
+    memcpy(p + DNS_H_QID, q->dnsq_id, 2);
+    p = dns_payload(p);
+    /* copy query dn */
+    p += dns_dntodn(q->dnsq_dn, p, DNS_MAXDN);
+    /* query type and class */
+    memcpy(p, q->dnsq_typcls, 4); p += 4;
+    /* add EDNS0 record. DO flag requires it */
+    if (q->dnsq_flags & DNS_SET_DO ||
+        (ctx->dnsc_udpbuf > DNS_MAXPACKET &&
+         !(q->dnsq_servnEDNS0 & (1 << servi)))) {
+      *p++ = 0;                        /* empty (root) DN */
+      p = dns_put16(p, DNS_T_OPT);
+      p = dns_put16(p, ctx->dnsc_udpbuf);
+      /* EDNS0 RCODE & VERSION; rest of the TTL field; RDLEN */
+      memset(p, 0, 2+2+2);
+      if (q->dnsq_flags & DNS_SET_DO) p[2] |= DNS_EF1_DO;
+      p += 2+2+2;
+      ctx->dnsc_pbuf[DNS_H_ARCNT2] = 1;
+    }
+    qlen = p - ctx->dnsc_pbuf;
+    assert(qlen <= ctx->dnsc_udpbuf);
+  }
+
+  /* send the query */
+  tries = 10;
+  while (sendto(ctx->dnsc_udpsock, (void*)ctx->dnsc_pbuf, qlen, 0,
+                &ctx->dnsc_serv[servi].sa, ctx->dnsc_salen) < 0) {
+    /*XXX just ignore the sendto() error for now and try again.
+     * In the future, it may be possible to retrieve the error code
+     * and find which operation/query failed.
+     *XXX try the next server too? (if ENETUNREACH is returned immediately)
+     */
+    if (--tries) continue;
+    /* if we can't send the query, fail it. */
+    dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0);
+    return -1;
+  }
+  DNS_DBGQ(ctx, q, 1,
+           &ctx->dnsc_serv[servi].sa, sizeof(union sockaddr_ns),
+           ctx->dnsc_pbuf, qlen);
+  q->dnsq_servwait |= 1 << servi;      /* expect reply from this ns */
+
+  q->dnsq_deadline = now +
+    (dns_find_serv(ctx, q) ? 1 : ctx->dnsc_timeout << q->dnsq_try);
+
+  /* move the query to the proper place, according to the new deadline */
+  qlist_remove(&ctx->dnsc_qactive, q);
+  { /* insert from the tail */
+    struct dns_query *p;
+    for(p = ctx->dnsc_qactive.tail; p; p = p->dnsq_prev)
+      if (p->dnsq_deadline <= q->dnsq_deadline)
+       break;
+    qlist_insert_after(&ctx->dnsc_qactive, q, p);
+  }
+
+  return 0;
+}
+
+/* send the query out using next available server
+ * and add it to the active list, or, if no servers available,
+ * end it.
+ */
+static void
+dns_send(struct dns_ctx *ctx, struct dns_query *q, time_t now) {
+
+  /* if we can't send the query, return TEMPFAIL even when searching:
+   * we can't be sure whenever the name we tried to search exists or not,
+   * so don't continue searching, or we may find the wrong name. */
+
+  if (!dns_find_serv(ctx, q)) {
+    /* no more servers in this iteration.  Try the next cycle */
+    q->dnsq_servi = 0; /* reset */
+    q->dnsq_try++;     /* next try */
+    if (q->dnsq_try >= ctx->dnsc_ntries ||
+        !dns_find_serv(ctx, q)) {
+      /* no more servers and tries, fail the query */
+      /* return TEMPFAIL even when searching: no more tries for this
+       * searchlist, and no single definitive reply (handled in dns_ioevent()
+       * in NOERROR or NXDOMAIN cases) => all nameservers failed to process
+       * current search list element, so we don't know whenever the name exists.
+       */
+      dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0);
+      return;
+    }
+  }
+
+  dns_send_this(ctx, q, q->dnsq_servi++, now);
+}
+
+static void dns_dummy_cb(struct dns_ctx *ctx, void *result, void *data) {
+  if (result) free(result);
+  data = ctx = 0;      /* used */
+}
+
+/* The (only, main, real) query submission routine.
+ * Allocate new query structure, initialize it, check validity of
+ * parameters, and add it to the head of the active list, without
+ * trying to send it (to be picked up on next event).
+ * Error return (without calling the callback routine) -
+ *  no memory or wrong parameters.
+ *XXX The `no memory' case probably should go to the callback anyway...
+ */
+struct dns_query *
+dns_submit_dn(struct dns_ctx *ctx,
+              dnscc_t *dn, int qcls, int qtyp, int flags,
+              dns_parse_fn *parse, dns_query_fn *cbck, void *data) {
+  struct dns_query *q;
+  SETCTXOPEN(ctx);
+  dns_assert_ctx(ctx);
+
+  q = calloc(sizeof(*q), 1);
+  if (!q) {
+    ctx->dnsc_qstatus = DNS_E_NOMEM;
+    return NULL;
+  }
+
+#ifndef NDEBUG
+  q->dnsq_ctx = ctx;
+#endif
+  q->dnsq_parse = parse;
+  q->dnsq_cbck = cbck ? cbck : dns_dummy_cb;
+  q->dnsq_cbdata = data;
+
+  q->dnsq_origdnl0 = dns_dntodn(dn, q->dnsq_dn, sizeof(q->dnsq_dn));
+  assert(q->dnsq_origdnl0 > 0);
+  --q->dnsq_origdnl0;          /* w/o the trailing 0 */
+  dns_put16(q->dnsq_typcls+0, qtyp);
+  dns_put16(q->dnsq_typcls+2, qcls);
+  q->dnsq_flags = (flags | ctx->dnsc_flags) & ~DNS_INTERNAL;
+
+  if (flags & DNS_NOSRCH ||
+      dns_dnlabels(q->dnsq_dn) > ctx->dnsc_ndots) {
+    q->dnsq_nxtsrch = flags & DNS_NOSRCH ?
+      ctx->dnsc_srchend /* end of the search list if no search requested */ :
+      ctx->dnsc_srchbuf /* beginning of the list, but try as-is first */;
+    q->dnsq_flags |= DNS_ASIS_DONE;
+    dns_newid(ctx, q);
+  }
+  else {
+    q->dnsq_nxtsrch = ctx->dnsc_srchbuf;
+    dns_next_srch(ctx, q);
+  }
+
+  /* q->dnsq_deadline is set to 0 (calloc above): the new query is
+   * "already expired" when first inserted into queue, so it's safe
+   * to insert it into the head of the list.  Next call to dns_timeouts()
+   * will actually send it.
+   */
+  qlist_add_head(&ctx->dnsc_qactive, q);
+  ++ctx->dnsc_nactive;
+  dns_request_utm(ctx, 0);
+
+  return q;
+}
+
+struct dns_query *
+dns_submit_p(struct dns_ctx *ctx,
+             const char *name, int qcls, int qtyp, int flags,
+             dns_parse_fn *parse, dns_query_fn *cbck, void *data) {
+  int isabs;
+  SETCTXOPEN(ctx);
+  if (dns_ptodn(name, 0, ctx->dnsc_pbuf, DNS_MAXDN, &isabs) <= 0) {
+    ctx->dnsc_qstatus = DNS_E_BADQUERY;
+    return NULL;
+  }
+  if (isabs)
+    flags |= DNS_NOSRCH;
+  return
+    dns_submit_dn(ctx, ctx->dnsc_pbuf, qcls, qtyp, flags, parse, cbck, data);
+}
+
+/* process readable fd condition.
+ * To be usable in edge-triggered environment, the routine
+ * should consume all input so it should loop over.
+ * Note it isn't really necessary to loop here, because
+ * an application may perform the loop just fine by it's own,
+ * but in this case we should return some sensitive result,
+ * to indicate when to stop calling and error conditions.
+ * Note also we may encounter all sorts of recvfrom()
+ * errors which aren't fatal, and at the same time we may
+ * loop forever if an error IS fatal.
+ */
+void dns_ioevent(struct dns_ctx *ctx, time_t now) {
+  int r;
+  unsigned servi;
+  struct dns_query *q;
+  dnsc_t *pbuf;
+  dnscc_t *pend, *pcur;
+  void *result;
+  union sockaddr_ns sns;
+  socklen_t slen;
+
+  SETCTX(ctx);
+  if (!CTXOPEN(ctx))
+    return;
+  dns_assert_ctx(ctx);
+  pbuf = ctx->dnsc_pbuf;
+
+  if (!now) now = time(NULL);
+
+again: /* receive the reply */
+
+  slen = sizeof(sns);
+  r = recvfrom(ctx->dnsc_udpsock, (void*)pbuf, ctx->dnsc_udpbuf,
+               MSG_DONTWAIT, &sns.sa, &slen);
+  if (r < 0) {
+    /*XXX just ignore recvfrom() errors for now.
+     * in the future it may be possible to determine which
+     * query failed and requeue it.
+     * Note there may be various error conditions, triggered
+     * by both local problems and remote problems.  It isn't
+     * quite trivial to determine whenever an error is local
+     * or remote.  On local errors, we should stop, while
+     * remote errors should be ignored (for now anyway).
+     */
+#ifdef WINDOWS
+    if (WSAGetLastError() == WSAEWOULDBLOCK)
+#else
+    if (errno == EAGAIN)
+#endif
+    {
+      dns_request_utm(ctx, now);
+      return;
+    }
+    goto again;
+  }
+
+  pend = pbuf + r;
+  pcur = dns_payload(pbuf);
+
+  /* check reply header */
+  if (pcur > pend || dns_numqd(pbuf) > 1 || dns_opcode(pbuf) != 0) {
+    DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r);
+    goto again;
+  }
+
+  /* find the matching query, by qID */
+  for (q = ctx->dnsc_qactive.head; ; q = q->dnsq_next) {
+    if (!q) {
+      /* no more requests: old reply? */
+      DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r);
+      goto again;
+    }
+    if (pbuf[DNS_H_QID1] == q->dnsq_id[0] &&
+        pbuf[DNS_H_QID2] == q->dnsq_id[1])
+      break;
+  }
+
+  /* if we have numqd, compare with our query qDN */
+  if (dns_numqd(pbuf)) {
+    /* decode the qDN */
+    dnsc_t dn[DNS_MAXDN];
+    if (dns_getdn(pbuf, &pcur, pend, dn, sizeof(dn)) < 0 ||
+        pcur + 4 > pend) {
+      DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r);
+      goto again;
+    }
+    if (!dns_dnequal(dn, q->dnsq_dn) ||
+        memcmp(pcur, q->dnsq_typcls, 4) != 0) {
+      /* not this query */
+      DNS_DBG(ctx, -5/*no matching query*/, &sns.sa, slen, pbuf, r);
+      goto again;
+    }
+    /* here, query match, and pcur points past qDN in query section in pbuf */
+  }
+  /* if no numqd, we only allow FORMERR rcode */
+  else if (dns_rcode(pbuf) != DNS_R_FORMERR) {
+    /* treat it as bad reply if !FORMERR */
+    DNS_DBG(ctx, -1/*bad reply*/, &sns.sa, slen, pbuf, r);
+    goto again;
+  }
+  else {
+    /* else it's FORMERR, handled below */
+  }
+
+  /* find server */
+#ifdef HAVE_IPv6
+  if (sns.sa.sa_family == AF_INET6 && slen >= sizeof(sns.sin6)) {
+    for(servi = 0; servi < ctx->dnsc_nserv; ++servi)
+      if (sin6_eq(ctx->dnsc_serv[servi].sin6, sns.sin6))
+        break;
+  }
+  else
+#endif
+  if (sns.sa.sa_family == AF_INET && slen >= sizeof(sns.sin)) {
+    for(servi = 0; servi < ctx->dnsc_nserv; ++servi)
+      if (sin_eq(ctx->dnsc_serv[servi].sin, sns.sin))
+        break;
+  }
+  else
+    servi = ctx->dnsc_nserv;
+
+  /* check if we expect reply from this server.
+   * Note we can receive reply from first try if we're already at next */
+  if (!(q->dnsq_servwait & (1 << servi))) { /* if ever asked this NS */
+    DNS_DBG(ctx, -2/*wrong server*/, &sns.sa, slen, pbuf, r);
+    goto again;
+  }
+
+  /* we got (some) reply for our query */
+
+  DNS_DBGQ(ctx, q, 0, &sns.sa, slen, pbuf, r);
+  q->dnsq_servwait &= ~(1 << servi);   /* don't expect reply from this serv */
+
+  /* process the RCODE */
+  switch(dns_rcode(pbuf)) {
+
+  case DNS_R_NOERROR:
+    if (dns_tc(pbuf)) {
+      /* possible truncation.  We can't deal with it. */
+      /*XXX for now, treat TC bit the same as SERVFAIL.
+       * It is possible to:
+       *  a) try to decode the reply - may be ANSWER section is ok;
+       *  b) check if server understands EDNS0, and if it is, and
+       *   answer still don't fit, end query.
+       */
+      break;
+    }
+    if (!dns_numan(pbuf)) {    /* no data of requested type */
+      if (dns_next_srch(ctx, q)) {
+        /* if we're searching, try next searchlist element,
+         * but remember NODATA reply. */
+        q->dnsq_flags |= DNS_SEEN_NODATA;
+        dns_send(ctx, q, now);
+      }
+      else
+        /* else - nothing to search any more - finish the query.
+         * It will be NODATA since we've seen a NODATA reply. */
+        dns_end_query(ctx, q, DNS_E_NODATA, 0);
+    }
+    /* we've got a positive reply here */
+    else if (q->dnsq_parse) {
+      /* if we have parsing routine, call it and return whatever it returned */
+      /* don't try to re-search if NODATA here.  For example,
+       * if we asked for A but only received CNAME.  Unless we'll
+       * someday do recursive queries.  And that's problematic too, since
+       * we may be dealing with specific AA-only nameservers for a given
+       * domain, but CNAME points elsewhere...
+       */
+      r = q->dnsq_parse(q->dnsq_dn, pbuf, pcur, pend, &result);
+      dns_end_query(ctx, q, r, r < 0 ? NULL : result);
+    }
+    /* else just malloc+copy the raw DNS reply */
+    else if ((result = malloc(r)) == NULL)
+      dns_end_query(ctx, q, DNS_E_NOMEM, NULL);
+    else {
+      memcpy(result, pbuf, r);
+      dns_end_query(ctx, q, r, result);
+    }
+    goto again;
+
+  case DNS_R_NXDOMAIN: /* Non-existing domain. */
+    if (dns_next_srch(ctx, q))
+      /* more search entries exists, try them. */
+      dns_send(ctx, q, now);
+    else
+      /* nothing to search anymore. End the query, returning either NODATA
+       * if we've seen it before, or NXDOMAIN if not. */
+      dns_end_query(ctx, q,
+           q->dnsq_flags & DNS_SEEN_NODATA ? DNS_E_NODATA : DNS_E_NXDOMAIN, 0);
+    goto again;
+
+  case DNS_R_FORMERR:
+  case DNS_R_NOTIMPL:
+    /* for FORMERR and NOTIMPL rcodes, if we tried EDNS0-enabled query,
+     * try w/o EDNS0. */
+    if (ctx->dnsc_udpbuf > DNS_MAXPACKET &&
+        !(q->dnsq_servnEDNS0 & (1 << servi))) {
+      /* we always trying EDNS0 first if enabled, and retry a given query
+       * if not available. Maybe it's better to remember inavailability of
+       * EDNS0 in ctx as a per-NS flag, and never try again for this NS.
+       * For long-running applications.. maybe they will change the nameserver
+       * while we're running? :)  Also, since FORMERR is the only rcode we
+       * allow to be header-only, and in this case the only check we do to
+       * find a query it belongs to is qID (not qDN+qCLS+qTYP), it's much
+       * easier to spoof and to force us to perform non-EDNS0 queries only...
+       */
+      q->dnsq_servnEDNS0 |= 1 << servi;
+      dns_send_this(ctx, q, servi, now);
+      goto again;
+    }
+    /* else we handle it the same as SERVFAIL etc */
+
+  case DNS_R_SERVFAIL:
+  case DNS_R_REFUSED:
+    /* for these rcodes, advance this request
+     * to the next server and reschedule */
+  default: /* unknown rcode? hmmm... */
+    break;
+  }
+
+  /* here, we received unexpected reply */
+  q->dnsq_servskip |= (1 << servi);    /* don't retry this server */
+
+  /* we don't expect replies from this server anymore.
+   * But there may be other servers.  Some may be still processing our
+   * query, and some may be left to try.
+   * We just ignore this reply and wait a bit more if some NSes haven't
+   * replied yet (dnsq_servwait != 0), and let the situation to be handled
+   * on next event processing.  Timeout for this query is set correctly,
+   * if not taking into account the one-second difference - we can try
+   * next server in the same iteration sooner.
+   */
+
+  /* try next server */
+  if (!q->dnsq_servwait) {
+    /* next retry: maybe some other servers will reply next time.
+     * dns_send() will end the query for us if no more servers to try.
+     * Note we can't continue with the next searchlist element here:
+     * we don't know if the current qdn exists or not, there's no definitive
+     * answer yet (which is seen in cases above).
+     *XXX standard resolver also tries as-is query in case all nameservers
+     * failed to process our query and if not tried before.  We don't do it.
+     */
+    dns_send(ctx, q, now);
+  }
+  else {
+    /* else don't do anything - not all servers replied yet */
+  }
+  goto again;
+
+}
+
+/* handle all timeouts */
+int dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now) {
+  /* this is a hot routine */
+  struct dns_query *q;
+
+  SETCTX(ctx);
+  dns_assert_ctx(ctx);
+
+  /* Pick up first entry from query list.
+   * If its deadline has passed, (re)send it
+   * (dns_send() will move it next in the list).
+   * If not, this is the query which determines the closest deadline.
+   */
+
+  q = ctx->dnsc_qactive.head;
+  if (!q)
+    return maxwait;
+  if (!now)
+    now = time(NULL);
+  do {
+    if (q->dnsq_deadline > now) { /* first non-expired query */
+      int w = (int)(q->dnsq_deadline - now);
+      if (maxwait < 0 || maxwait > w)
+        maxwait = w;
+      break;
+    }
+    else {
+      /* process expired deadline */
+      dns_send(ctx, q, now);
+    }
+  } while((q = ctx->dnsc_qactive.head) != NULL);
+
+  dns_request_utm(ctx, now); /* update timer with new deadline */
+  return maxwait;
+}
+
+struct dns_resolve_data {
+  int   dnsrd_done;
+  void *dnsrd_result;
+};
+
+static void dns_resolve_cb(struct dns_ctx *ctx, void *result, void *data) {
+  struct dns_resolve_data *d = data;
+  d->dnsrd_result = result;
+  d->dnsrd_done = 1;
+  ctx = ctx;
+}
+
+void *dns_resolve(struct dns_ctx *ctx, struct dns_query *q) {
+  time_t now;
+  struct dns_resolve_data d;
+  int n;
+  SETCTXOPEN(ctx);
+
+  if (!q)
+    return NULL;
+
+  assert(ctx == q->dnsq_ctx);
+  dns_assert_ctx(ctx);
+  /* do not allow re-resolving syncronous queries */
+  assert(q->dnsq_cbck != dns_resolve_cb && "can't resolve syncronous query");
+  if (q->dnsq_cbck == dns_resolve_cb) {
+    ctx->dnsc_qstatus = DNS_E_BADQUERY;
+    return NULL;
+  }
+  q->dnsq_cbck = dns_resolve_cb;
+  q->dnsq_cbdata = &d;
+  d.dnsrd_done = 0;
+
+  now = time(NULL);
+  while(!d.dnsrd_done && (n = dns_timeouts(ctx, -1, now)) >= 0) {
+#ifdef HAVE_POLL
+    struct pollfd pfd;
+    pfd.fd = ctx->dnsc_udpsock;
+    pfd.events = POLLIN;
+    n = poll(&pfd, 1, n * 1000);
+#else
+    fd_set rfd;
+    struct timeval tv;
+    FD_ZERO(&rfd);
+    FD_SET(ctx->dnsc_udpsock, &rfd);
+    tv.tv_sec = n; tv.tv_usec = 0;
+    n = select(ctx->dnsc_udpsock + 1, &rfd, NULL, NULL, &tv);
+#endif
+    now = time(NULL);
+    if (n > 0)
+      dns_ioevent(ctx, now);
+  }
+
+  return d.dnsrd_result;
+}
+
+void *dns_resolve_dn(struct dns_ctx *ctx,
+                     dnscc_t *dn, int qcls, int qtyp, int flags,
+                     dns_parse_fn *parse) {
+  return
+    dns_resolve(ctx,
+      dns_submit_dn(ctx, dn, qcls, qtyp, flags, parse, NULL, NULL));
+}
+
+void *dns_resolve_p(struct dns_ctx *ctx,
+                    const char *name, int qcls, int qtyp, int flags,
+                    dns_parse_fn *parse) {
+  return
+    dns_resolve(ctx,
+      dns_submit_p(ctx, name, qcls, qtyp, flags, parse, NULL, NULL));
+}
+
+int dns_cancel(struct dns_ctx *ctx, struct dns_query *q) {
+  SETCTX(ctx);
+  dns_assert_ctx(ctx);
+  assert(q->dnsq_ctx == ctx);
+  /* do not allow cancelling syncronous queries */
+  assert(q->dnsq_cbck != dns_resolve_cb && "can't cancel syncronous query");
+  if (q->dnsq_cbck == dns_resolve_cb)
+    return (ctx->dnsc_qstatus = DNS_E_BADQUERY);
+  qlist_remove(&ctx->dnsc_qactive, q);
+  --ctx->dnsc_nactive;
+  dns_request_utm(ctx, 0);
+  return 0;
+}
+
diff --git a/3rdparty/udns/udns_rr_a.c b/3rdparty/udns/udns_rr_a.c
new file mode 100644 (file)
index 0000000..4fdcbf9
--- /dev/null
@@ -0,0 +1,123 @@
+/* udns_rr_a.c
+   parse/query A/AAAA IN records
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#ifndef WINDOWS
+# include <sys/types.h>
+# include <netinet/in.h>
+#endif
+#include "udns.h"
+
+/* here, we use common routine to parse both IPv4 and IPv6 addresses.
+ */
+
+/* this structure should match dns_rr_a[46] */
+struct dns_rr_a {
+  dns_rr_common(dnsa);
+  unsigned char *dnsa_addr;
+};
+
+static int
+dns_parse_a(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
+            void **result, unsigned dsize) {
+  struct dns_rr_a *ret;
+  struct dns_parse p;
+  struct dns_rr rr;
+  int r;
+
+  /* first, validate and count number of addresses */
+  dns_initparse(&p, qdn, pkt, cur, end);
+  while((r = dns_nextrr(&p, &rr)) > 0)
+    if (rr.dnsrr_dsz != dsize)
+      return DNS_E_PROTOCOL;
+  if (r < 0)
+    return DNS_E_PROTOCOL;
+  else if (!p.dnsp_nrr)
+    return DNS_E_NODATA;
+
+  ret = malloc(sizeof(*ret) + dsize * p.dnsp_nrr + dns_stdrr_size(&p));
+  if (!ret)
+    return DNS_E_NOMEM;
+
+  ret->dnsa_nrr = p.dnsp_nrr;
+  ret->dnsa_addr = (unsigned char*)(ret+1);
+
+  /* copy the RRs */
+  for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r)
+    memcpy(ret->dnsa_addr + dsize * r, rr.dnsrr_dptr, dsize);
+
+  dns_stdrr_finish((struct dns_rr_null *)ret,
+                   (char *)(ret->dnsa_addr + dsize * p.dnsp_nrr), &p);
+  *result = ret;
+  return 0;
+}
+
+int
+dns_parse_a4(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
+             void **result) {
+#ifdef AF_INET
+  assert(sizeof(struct in_addr) == 4);
+#endif
+  assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_A);
+  return dns_parse_a(qdn, pkt, cur, end, result, 4);
+}
+
+struct dns_query *
+dns_submit_a4(struct dns_ctx *ctx, const char *name, int flags,
+              dns_query_a4_fn *cbck, void *data) {
+  return
+    dns_submit_p(ctx, name, DNS_C_IN, DNS_T_A, flags,
+                 dns_parse_a4, (dns_query_fn*)cbck, data);
+}
+
+struct dns_rr_a4 *
+dns_resolve_a4(struct dns_ctx *ctx, const char *name, int flags) {
+  return (struct dns_rr_a4 *)
+    dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_A, flags, dns_parse_a4);
+}
+
+int
+dns_parse_a6(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
+             void **result) {
+#ifdef AF_INET6
+  assert(sizeof(struct in6_addr) == 16);
+#endif
+  assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_AAAA);
+  return dns_parse_a(qdn, pkt, cur, end, result, 16);
+}
+
+struct dns_query *
+dns_submit_a6(struct dns_ctx *ctx, const char *name, int flags,
+              dns_query_a6_fn *cbck, void *data) {
+  return
+    dns_submit_p(ctx, name, DNS_C_IN, DNS_T_AAAA, flags,
+                 dns_parse_a6, (dns_query_fn*)cbck, data);
+}
+
+struct dns_rr_a6 *
+dns_resolve_a6(struct dns_ctx *ctx, const char *name, int flags) {
+  return (struct dns_rr_a6 *)
+    dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_AAAA, flags, dns_parse_a6);
+}
diff --git a/3rdparty/udns/udns_rr_mx.c b/3rdparty/udns/udns_rr_mx.c
new file mode 100644 (file)
index 0000000..0904e01
--- /dev/null
@@ -0,0 +1,91 @@
+/* udns_rr_mx.c
+   parse/query MX IN records
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "udns.h"
+
+int
+dns_parse_mx(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
+             void **result) {
+  struct dns_rr_mx *ret;
+  struct dns_parse p;
+  struct dns_rr rr;
+  int r, l;
+  char *sp;
+  dnsc_t mx[DNS_MAXDN];
+
+  assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_MX);
+
+  /* first, validate the answer and count size of the result */
+  l = 0;
+  dns_initparse(&p, qdn, pkt, cur, end);
+  while((r = dns_nextrr(&p, &rr)) > 0) {
+    cur = rr.dnsrr_dptr + 2;
+    r = dns_getdn(pkt, &cur, end, mx, sizeof(mx));
+    if (r <= 0 || cur != rr.dnsrr_dend)
+      return DNS_E_PROTOCOL;
+    l += dns_dntop_size(mx);
+  }
+  if (r < 0)
+    return DNS_E_PROTOCOL;
+  if (!p.dnsp_nrr)
+    return DNS_E_NODATA;
+
+  /* next, allocate and set up result */
+  l += dns_stdrr_size(&p);
+  ret = malloc(sizeof(*ret) + sizeof(struct dns_mx) * p.dnsp_nrr + l);
+  if (!ret)
+    return DNS_E_NOMEM;
+  ret->dnsmx_nrr = p.dnsp_nrr;
+  ret->dnsmx_mx = (struct dns_mx *)(ret+1);
+
+  /* and 3rd, fill in result, finally */
+  sp = (char*)(ret->dnsmx_mx + p.dnsp_nrr);
+  for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) {
+    ret->dnsmx_mx[r].name = sp;
+    cur = rr.dnsrr_dptr;
+    ret->dnsmx_mx[r].priority = dns_get16(cur);
+    cur += 2;
+    dns_getdn(pkt, &cur, end, mx, sizeof(mx));
+    sp += dns_dntop(mx, sp, DNS_MAXNAME);
+  }
+  dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p);
+  *result = ret;
+  return 0;
+}
+
+struct dns_query *
+dns_submit_mx(struct dns_ctx *ctx, const char *name, int flags,
+              dns_query_mx_fn *cbck, void *data) {
+  return
+    dns_submit_p(ctx, name, DNS_C_IN, DNS_T_MX, flags,
+                 dns_parse_mx, (dns_query_fn *)cbck, data);
+}
+
+struct dns_rr_mx *
+dns_resolve_mx(struct dns_ctx *ctx, const char *name, int flags) {
+  return (struct dns_rr_mx *)
+    dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_MX, flags, dns_parse_mx);
+}
diff --git a/3rdparty/udns/udns_rr_naptr.c b/3rdparty/udns/udns_rr_naptr.c
new file mode 100644 (file)
index 0000000..da30069
--- /dev/null
@@ -0,0 +1,128 @@
+/* udns_rr_naptr.c
+   parse/query NAPTR IN records
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   Copyright (C) 2006  Mikael Magnusson <mikma@users.sourceforge.net>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "udns.h"
+
+/* Get a single string for NAPTR record, pretty much like a DN label.
+ * String length is in first byte in *cur, so it can't be >255.
+ */
+static int dns_getstr(dnscc_t **cur, dnscc_t *ep, char *buf)
+{
+  unsigned l;
+  dnscc_t *cp = *cur;
+
+  l = *cp++;
+  if (cp + l > ep)
+    return DNS_E_PROTOCOL;
+  if (buf) {
+    memcpy(buf, cp, l);
+    buf[l] = '\0';
+  }
+  cp += l;
+
+  *cur = cp;
+  return l + 1;
+}
+
+int
+dns_parse_naptr(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
+                void **result) {
+  struct dns_rr_naptr *ret;
+  struct dns_parse p;
+  struct dns_rr rr;
+  int r, l;
+  char *sp;
+  dnsc_t dn[DNS_MAXDN];
+
+  assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_NAPTR);
+
+  /* first, validate the answer and count size of the result */
+  l = 0;
+  dns_initparse(&p, qdn, pkt, cur, end);
+  while((r = dns_nextrr(&p, &rr)) > 0) {
+    int i;
+    dnscc_t *ep = rr.dnsrr_dend;
+
+    /* first 4 bytes: order & preference */
+    cur = rr.dnsrr_dptr + 4;
+
+    /* flags, services and regexp */
+    for (i = 0; i < 3; i++) {
+      r = dns_getstr(&cur, ep, NULL);
+      if (r < 0)
+        return r;
+      l += r;
+    }
+    /* replacement */
+    r = dns_getdn(pkt, &cur, end, dn, sizeof(dn));
+    if (r <= 0 || cur != rr.dnsrr_dend)
+      return DNS_E_PROTOCOL;
+    l += dns_dntop_size(dn);
+  }
+  if (r < 0)
+    return DNS_E_PROTOCOL;
+  if (!p.dnsp_nrr)
+    return DNS_E_NODATA;
+
+  /* next, allocate and set up result */
+  l += dns_stdrr_size(&p);
+  ret = malloc(sizeof(*ret) + sizeof(struct dns_naptr) * p.dnsp_nrr + l);
+  if (!ret)
+    return DNS_E_NOMEM;
+  ret->dnsnaptr_nrr = p.dnsp_nrr;
+  ret->dnsnaptr_naptr = (struct dns_naptr *)(ret+1);
+
+  /* and 3rd, fill in result, finally */
+  sp = (char*)(&ret->dnsnaptr_naptr[p.dnsp_nrr]);
+  for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) {
+    cur = rr.dnsrr_dptr;
+    ret->dnsnaptr_naptr[r].order = dns_get16(cur); cur += 2;
+    ret->dnsnaptr_naptr[r].preference = dns_get16(cur); cur += 2;
+    sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].flags = sp));
+    sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].service = sp));
+    sp += dns_getstr(&cur, end, (ret->dnsnaptr_naptr[r].regexp = sp));
+    dns_getdn(pkt, &cur, end, dn, sizeof(dn));
+    sp += dns_dntop(dn, (ret->dnsnaptr_naptr[r].replacement = sp), DNS_MAXNAME);
+  }
+  dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p);
+  *result = ret;
+  return 0;
+}
+
+struct dns_query *
+dns_submit_naptr(struct dns_ctx *ctx, const char *name, int flags,
+                 dns_query_naptr_fn *cbck, void *data) {
+  return
+    dns_submit_p(ctx, name, DNS_C_IN, DNS_T_NAPTR, flags,
+                 dns_parse_naptr, (dns_query_fn *)cbck, data);
+}
+
+struct dns_rr_naptr *
+dns_resolve_naptr(struct dns_ctx *ctx, const char *name, int flags) {
+  return (struct dns_rr_naptr *)
+    dns_resolve_p(ctx, name, DNS_C_IN, DNS_T_NAPTR, flags, dns_parse_naptr);
+}
diff --git a/3rdparty/udns/udns_rr_ptr.c b/3rdparty/udns/udns_rr_ptr.c
new file mode 100644 (file)
index 0000000..1f682ae
--- /dev/null
@@ -0,0 +1,109 @@
+/* udns_rr_ptr.c
+   parse/query PTR records
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include "udns.h"
+
+int
+dns_parse_ptr(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
+              void **result) {
+  struct dns_rr_ptr *ret;
+  struct dns_parse p;
+  struct dns_rr rr;
+  int r, l, c;
+  char *sp;
+  dnsc_t ptr[DNS_MAXDN];
+
+  assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_PTR);
+
+  /* first, validate the answer and count size of the result */
+  l = c = 0;
+  dns_initparse(&p, qdn, pkt, cur, end);
+  while((r = dns_nextrr(&p, &rr)) > 0) {
+    cur = rr.dnsrr_dptr;
+    r = dns_getdn(pkt, &cur, end, ptr, sizeof(ptr));
+    if (r <= 0 || cur != rr.dnsrr_dend)
+      return DNS_E_PROTOCOL;
+    l += dns_dntop_size(ptr);
+    ++c;
+  }
+  if (r < 0)
+    return DNS_E_PROTOCOL;
+  if (!c)
+    return DNS_E_NODATA;
+
+  /* next, allocate and set up result */
+  ret = malloc(sizeof(*ret) + sizeof(char **) * c + l + dns_stdrr_size(&p));
+  if (!ret)
+    return DNS_E_NOMEM;
+  ret->dnsptr_nrr = c;
+  ret->dnsptr_ptr = (char **)(ret+1);
+
+  /* and 3rd, fill in result, finally */
+  sp = (char*)(ret->dnsptr_ptr + c);
+  c = 0;
+  dns_rewind(&p, qdn);
+  while((r = dns_nextrr(&p, &rr)) > 0) {
+    ret->dnsptr_ptr[c] = sp;
+    cur = rr.dnsrr_dptr;
+    dns_getdn(pkt, &cur, end, ptr, sizeof(ptr));
+    sp += dns_dntop(ptr, sp, DNS_MAXNAME);
+    ++c;
+  }
+  dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p);
+  *result = ret;
+  return 0;
+}
+
+struct dns_query *
+dns_submit_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr,
+                 dns_query_ptr_fn *cbck, void *data) {
+  dnsc_t dn[DNS_A4RSIZE];
+  dns_a4todn(addr, 0, dn, sizeof(dn));
+  return
+    dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_PTR, DNS_NOSRCH,
+                  dns_parse_ptr, (dns_query_fn *)cbck, data);
+}
+
+struct dns_rr_ptr *
+dns_resolve_a4ptr(struct dns_ctx *ctx, const struct in_addr *addr) {
+  return (struct dns_rr_ptr *)
+    dns_resolve(ctx, dns_submit_a4ptr(ctx, addr, NULL, NULL));
+}
+
+struct dns_query *
+dns_submit_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr,
+                 dns_query_ptr_fn *cbck, void *data) {
+  dnsc_t dn[DNS_A6RSIZE];
+  dns_a6todn(addr, 0, dn, sizeof(dn));
+  return
+    dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_PTR, DNS_NOSRCH,
+                  dns_parse_ptr, (dns_query_fn *)cbck, data);
+}
+
+struct dns_rr_ptr *
+dns_resolve_a6ptr(struct dns_ctx *ctx, const struct in6_addr *addr) {
+  return (struct dns_rr_ptr *)
+    dns_resolve(ctx, dns_submit_a6ptr(ctx, addr, NULL, NULL));
+}
diff --git a/3rdparty/udns/udns_rr_srv.c b/3rdparty/udns/udns_rr_srv.c
new file mode 100644 (file)
index 0000000..dfba465
--- /dev/null
@@ -0,0 +1,155 @@
+/* udns_rr_srv.c
+   parse/query SRV IN (rfc2782) records
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+   Copyright 2005 Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
+
+   2005-09-11:
+   Changed MX parser file into a SRV parser file
+
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "udns.h"
+
+int
+dns_parse_srv(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
+              void **result) {
+  struct dns_rr_srv *ret;
+  struct dns_parse p;
+  struct dns_rr rr;
+  int r, l;
+  char *sp;
+  dnsc_t srv[DNS_MAXDN];
+
+  assert(dns_get16(cur+2) == DNS_C_IN && dns_get16(cur+0) == DNS_T_SRV);
+
+  /* first, validate the answer and count size of the result */
+  l = 0;
+  dns_initparse(&p, qdn, pkt, cur, end);
+  while((r = dns_nextrr(&p, &rr)) > 0) {
+    cur = rr.dnsrr_dptr + 6;
+    r = dns_getdn(pkt, &cur, end, srv, sizeof(srv));
+    if (r <= 0 || cur != rr.dnsrr_dend)
+      return DNS_E_PROTOCOL;
+    l += dns_dntop_size(srv);
+  }
+  if (r < 0)
+    return DNS_E_PROTOCOL;
+  if (!p.dnsp_nrr)
+    return DNS_E_NODATA;
+
+  /* next, allocate and set up result */
+  l += dns_stdrr_size(&p);
+  ret = malloc(sizeof(*ret) + sizeof(struct dns_srv) * p.dnsp_nrr + l);
+  if (!ret)
+    return DNS_E_NOMEM;
+  ret->dnssrv_nrr = p.dnsp_nrr;
+  ret->dnssrv_srv = (struct dns_srv *)(ret+1);
+
+  /* and 3rd, fill in result, finally */
+  sp = (char*)(ret->dnssrv_srv + p.dnsp_nrr);
+  for (dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr); ++r) {
+    ret->dnssrv_srv[r].name = sp;
+    cur = rr.dnsrr_dptr;
+    ret->dnssrv_srv[r].priority = dns_get16(cur);
+    ret->dnssrv_srv[r].weight = dns_get16(cur+2);
+    ret->dnssrv_srv[r].port = dns_get16(cur+4);
+    cur += 6;
+    dns_getdn(pkt, &cur, end, srv, sizeof(srv));
+    sp += dns_dntop(srv, sp, DNS_MAXNAME);
+  }
+  dns_stdrr_finish((struct dns_rr_null *)ret, sp, &p);
+  *result = ret;
+  return 0;
+}
+
+/* Add a single service or proto name prepending an undescore (_),
+ * according to rfc2782 rules.
+ * Return 0 or the label length.
+ * Routing assumes dn holds enouth space for a single DN label. */
+static int add_sname(dnsc_t *dn, const char *sn) {
+  int l = dns_ptodn(sn, 0, dn + 1, DNS_MAXLABEL-1, NULL);
+  if (l <= 1 || l - 2 != dn[1])
+    /* Should we really check if sn is exactly one label?  Do we care? */
+    return 0;
+  dn[0] = l - 1;
+  dn[1] = '_';
+  return l;
+}
+
+/* Construct a domain name for SRV query from the given name, service and proto.
+ * The code allows any combinations of srv and proto (both are non-NULL,
+ * both NULL, or either one is non-NULL).  Whenever it makes any sense or not
+ * is left as an exercise to programmer.
+ * Return negative value on error (malformed query) or addition query flag(s).
+ */
+static int
+build_srv_dn(dnsc_t *dn, const char *name, const char *srv, const char *proto)
+{
+  int p = 0, l, isabs;
+  if (srv) {
+    l = add_sname(dn + p, srv);
+    if (!l)
+      return -1;
+    p += l;
+  }
+  if (proto) {
+    l = add_sname(dn + p, proto);
+    if (!l)
+      return -1;
+    p += l;
+  }
+  l = dns_ptodn(name, 0, dn + p, DNS_MAXDN - p, &isabs);
+  if (l < 0)
+    return -1;
+  return isabs ? DNS_NOSRCH : 0;
+}
+
+struct dns_query *
+dns_submit_srv(struct dns_ctx *ctx,
+               const char *name, const char *srv, const char *proto,
+               int flags, dns_query_srv_fn *cbck, void *data) {
+  dnsc_t dn[DNS_MAXDN];
+  int r = build_srv_dn(dn, name, srv, proto);
+  if (r < 0) {
+    dns_setstatus (ctx, DNS_E_BADQUERY);
+    return NULL;
+  }
+  return
+    dns_submit_dn(ctx, dn, DNS_C_IN, DNS_T_SRV, flags | r,
+                  dns_parse_srv, (dns_query_fn *)cbck, data);
+}
+
+struct dns_rr_srv *
+dns_resolve_srv(struct dns_ctx *ctx,
+                const char *name, const char *srv, const char *proto, int flags)
+{
+  dnsc_t dn[DNS_MAXDN];
+  int r = build_srv_dn(dn, name, srv, proto);
+  if (r < 0) {
+    dns_setstatus(ctx, DNS_E_BADQUERY);
+    return NULL;
+  }
+  return (struct dns_rr_srv *)
+    dns_resolve_dn(ctx, dn, DNS_C_IN, DNS_T_SRV, flags | r, dns_parse_srv);
+}
diff --git a/3rdparty/udns/udns_rr_txt.c b/3rdparty/udns/udns_rr_txt.c
new file mode 100644 (file)
index 0000000..97f1dfb
--- /dev/null
@@ -0,0 +1,98 @@
+/* udns_rr_txt.c
+   parse/query TXT records
+
+   Copyright (C) 2005  Michael Tokarev <mjt@corpit.ru>
+   This file is part of UDNS library, an async DNS stub resolver.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library, in file named COPYING.LGPL; if not,
+   write to the Free Software Foundation, Inc., 59 Temple Place,
+   Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "udns.h"
+
+int
+dns_parse_txt(dnscc_t *qdn, dnscc_t *pkt, dnscc_t *cur, dnscc_t *end,
+              void **result) {
+  struct dns_rr_txt *ret;
+  struct dns_parse p;
+  struct dns_rr rr;
+  int r, l;
+  dnsc_t *sp;
+  dnscc_t *cp, *ep;
+
+  assert(dns_get16(cur+0) == DNS_T_TXT);
+
+  /* first, validate the answer and count size of the result */
+  l = 0;
+  dns_initparse(&p, qdn, pkt, cur, end);
+  while((r = dns_nextrr(&p, &rr)) > 0) {
+    cp = rr.dnsrr_dptr; ep = rr.dnsrr_dend;
+    while(cp < ep) {
+      r = *cp++;
+      if (cp + r > ep)
+        return DNS_E_PROTOCOL;
+      l += r;
+      cp += r;
+    }
+  }
+  if (r < 0)
+    return DNS_E_PROTOCOL;
+  if (!p.dnsp_nrr)
+    return DNS_E_NODATA;
+
+  /* next, allocate and set up result */
+  l +=  (sizeof(struct dns_txt) + 1) * p.dnsp_nrr + dns_stdrr_size(&p);
+  ret = malloc(sizeof(*ret) + l);
+  if (!ret)
+    return DNS_E_NOMEM;
+  ret->dnstxt_nrr = p.dnsp_nrr;
+  ret->dnstxt_txt = (struct dns_txt *)(ret+1);
+
+  /* and 3rd, fill in result, finally */
+  sp = (dnsc_t*)(ret->dnstxt_txt + p.dnsp_nrr);
+  for(dns_rewind(&p, qdn), r = 0; dns_nextrr(&p, &rr) > 0; ++r) {
+    ret->dnstxt_txt[r].txt = sp;
+    cp = rr.dnsrr_dptr; ep = rr.dnsrr_dend;
+    while(cp < ep) {
+      l = *cp++;
+      memcpy(sp, cp, l);
+      sp += l;
+      cp += l;
+    }
+    ret->dnstxt_txt[r].len = sp - ret->dnstxt_txt[r].txt;
+    *sp++ = '\0';
+  }
+  dns_stdrr_finish((struct dns_rr_null *)ret, (char*)sp, &p);
+  *result = ret;
+  return 0;
+}
+
+struct dns_query *
+dns_submit_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags,
+               dns_query_txt_fn *cbck, void *data) {
+  return
+    dns_submit_p(ctx, name, qcls, DNS_T_TXT, flags,
+                 dns_parse_txt, (dns_query_fn *)cbck, data);
+}
+
+struct dns_rr_txt *
+dns_resolve_txt(struct dns_ctx *ctx, const char *name, int qcls, int flags) {
+  return (struct dns_rr_txt *)
+    dns_resolve_p(ctx, name, qcls, DNS_T_TXT, flags, dns_parse_txt);
+}
index dffb095738fd5b3cf37d813e3b84008301acdbad..7329f0bf5c9acfae6959ce8090be498d3e9ba12a 100644 (file)
@@ -118,6 +118,7 @@ option(ENABLE_TESTS     "Set to OFF to disable building SimGear's test applicati
 option(ENABLE_SOUND     "Set to OFF to disable building SimGear's sound support" ON)
 option(ENABLE_PKGUTIL   "Set to ON to build the sg_pkgutil application (default)" ON)
 option(ENABLE_CURL      "Set to ON to use libCurl as the HTTP client backend" OFF)
+option(ENABLE_DNS       "Set to ON to use udns library and DNS service resolver" OFF)
 
 if (MSVC)
   GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
@@ -420,6 +421,13 @@ endif()
 install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h  DESTINATION include/simgear/)
 
 include_directories(3rdparty/utf8/source)
+if (ENABLE_DNS)
+    message(STATUS "DNS resolver: ENABLED")
+    include_directories(3rdparty/udns)
+else()
+    message(STATUS "DNS resolver: DISABLED")
+endif()
+
 
 add_subdirectory(3rdparty)
 add_subdirectory(simgear)
index 3e8775655243e3b4eb5542630785da36bd50540d..40675d83acd481a1a0aa39b029421aecea7fb34b 100644 (file)
@@ -55,6 +55,11 @@ if (NOT ENABLE_CURL)
   list(APPEND HEADERS HTTPContentDecode.hxx)
 endif()
 
+if(ENABLE_DNS)
+  list(APPEND SOURCES DNSClient.cxx)
+  list(APPEND HEADERS DNSClient.hxx)
+endif()
+
 simgear_component(io io "${SOURCES}" "${HEADERS}")
 
 if(ENABLE_TESTS)
@@ -70,6 +75,12 @@ target_link_libraries(test_http ${TEST_LIBS})
 
 add_test(http ${EXECUTABLE_OUTPUT_PATH}/test_http)
 
+if(ENABLE_DNS)
+       add_executable(test_dns test_DNS.cxx)
+       target_link_libraries(test_dns ${TEST_LIBS})
+       add_test(dns ${EXECUTABLE_OUTPUT_PATH}/test_dns)
+endif()
+
 add_executable(httpget httpget.cxx)
 target_link_libraries(httpget ${TEST_LIBS})
 
diff --git a/simgear/io/DNSClient.cxx b/simgear/io/DNSClient.cxx
new file mode 100644 (file)
index 0000000..813f2c1
--- /dev/null
@@ -0,0 +1,120 @@
+/**
+ * \file DNSClient.cxx - simple DNS resolver client engine for SimGear
+ */
+
+// Written by James Turner
+//
+// Copyright (C) 2016 Torsten Dreyer - torsten (at) t3r (dot) de
+//
+// This library is free software; you can redistribute it and/or
+// modify it 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.
+//
+// This library 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
+// Library 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+
+#include "DNSClient.hxx"
+#include "udns.h"
+#include <simgear/debug/logstream.hxx>
+
+namespace simgear {
+
+namespace DNS {
+
+class Client::ClientPrivate {
+public:
+       ClientPrivate() {
+               if (dns_init(NULL, 0) < 0)
+                       SG_LOG(SG_IO, SG_ALERT, "Can't init udns library" );
+
+               if( dns_open(NULL) < 0 )
+                       SG_LOG(SG_IO, SG_ALERT, "Can't open udns context" );
+       }
+
+       ~ClientPrivate() {
+               dns_close(NULL);
+       }
+};
+
+Request::Request( const std::string & dn ) :
+               _dn(dn),
+               _type(DNS_T_ANY),
+               _complete(false)
+{
+}
+
+Request::~Request()
+{
+}
+
+NAPTRRequest::NAPTRRequest( const std::string & dn ) :
+               Request(dn)
+{
+       _type = DNS_T_NAPTR;
+}
+
+static void dnscbNAPTR(struct dns_ctx *ctx, struct dns_rr_naptr *result, void *data)
+{
+       NAPTRRequest * r = static_cast<NAPTRRequest*>(data);
+       if (result) {
+               r->cname = result->dnsnaptr_cname;
+               r->qname = result->dnsnaptr_qname;
+               r->ttl = result->dnsnaptr_ttl;
+               for (int i = 0; i < result->dnsnaptr_nrr; i++) {
+                       NAPTRRequest::NAPTR_ptr naptr(new NAPTRRequest::NAPTR);
+                       r->entries.push_back(naptr);
+                       naptr->order = result->dnsnaptr_naptr[i].order;
+                       naptr->preference = result->dnsnaptr_naptr[i].preference;
+                       naptr->flags = result->dnsnaptr_naptr[i].flags;
+                       naptr->service = result->dnsnaptr_naptr[i].service;
+                       naptr->regexp = result->dnsnaptr_naptr[i].regexp;
+                       naptr->replacement = result->dnsnaptr_naptr[i].replacement;
+               }
+               free(result);
+       }
+       r->setComplete();
+}
+
+void NAPTRRequest::submit()
+{
+       if (!dns_submit_naptr(NULL, getDn().c_str(), 0, dnscbNAPTR, this )) {
+               SG_LOG(SG_IO, SG_ALERT, "Can't submit dns request for " << getDn());
+               return;
+       }
+}
+
+
+Client::~Client()
+{
+}
+
+Client::Client() :
+               d(new ClientPrivate)
+{
+}
+
+void Client::makeRequest(const Request_ptr& r)
+{
+       r->submit();
+}
+
+
+void Client::update(int waitTimeout)
+{
+       time_t now = time(NULL);
+       if( dns_timeouts( NULL, waitTimeout, now ) < 0 )
+               return;
+    dns_ioevent(NULL, now);
+}
+
+} // of namespace DNS
+
+} // of namespace simgear
diff --git a/simgear/io/DNSClient.hxx b/simgear/io/DNSClient.hxx
new file mode 100644 (file)
index 0000000..5a7604d
--- /dev/null
@@ -0,0 +1,102 @@
+/**
+ * \file DNSClient.hxx - simple DNS resolver client for SimGear
+ */
+
+// Written by Torsten Dreyer
+//
+// Copyright (C) 2016 Torsten Dreyer - torsten (at) t3r (dot) de
+//
+// This library is free software; you can redistribute it and/or
+// modify it 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.
+//
+// This library 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
+// Library 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//
+
+#ifndef SG_DNS_CLIENT_HXX
+#define SG_DNS_CLIENT_HXX
+
+#include <memory> // for std::auto_ptr
+#include <string>
+#include <vector>
+#include <simgear/structure/SGReferenced.hxx>
+#include <simgear/structure/SGSharedPtr.hxx>
+
+namespace simgear
+{
+
+namespace DNS
+{
+
+class Request : public SGReferenced
+{
+public:
+       Request( const std::string & dn );
+       virtual ~Request();
+       std::string getDn() const { return _dn; }
+       int getType() const { return _type; }
+       bool complete() const { return _complete; }
+       void setComplete( bool b = true ) { _complete = b; }
+
+       virtual void submit() = 0;
+protected:
+       std::string _dn;
+       int _type;
+       bool _complete;
+};
+
+class NAPTRRequest : public Request
+{
+public:
+       NAPTRRequest( const std::string & dn );
+       virtual void submit();
+
+       struct NAPTR : SGReferenced {
+               int order;
+               int preference;
+               std::string flags;
+               std::string service;
+               std::string regexp;
+               std::string replacement;
+       };
+       typedef SGSharedPtr<NAPTR> NAPTR_ptr;
+       typedef std::vector<NAPTR_ptr> NAPTR_list;
+       std::string cname;
+       std::string qname;
+       unsigned ttl;
+       NAPTR_list entries;
+};
+
+typedef SGSharedPtr<Request> Request_ptr;
+
+class Client
+{
+public:
+    Client();
+    ~Client();
+
+    void update(int waitTimeout = 0);
+
+    void makeRequest(const Request_ptr& r);
+
+//    void cancelRequest(const Request_ptr& r, std::string reason = std::string());
+
+private:
+
+    class ClientPrivate;
+    std::auto_ptr<ClientPrivate> d;
+};
+
+} // of namespace DNS
+
+} // of namespace simgear
+
+#endif // of SG_DNS_CLIENT_HXX
diff --git a/simgear/io/test_DNS.cxx b/simgear/io/test_DNS.cxx
new file mode 100644 (file)
index 0000000..df2a10e
--- /dev/null
@@ -0,0 +1,68 @@
+#include <cstdlib>
+
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <errno.h>
+
+#include <boost/algorithm/string/case_conv.hpp>
+
+#include <simgear/simgear_config.h>
+
+#include "DNSClient.hxx"
+
+#include "test_DNS.hxx"
+
+#include <simgear/debug/logstream.hxx>
+
+using std::cout;
+using std::cerr;
+using std::endl;
+
+using namespace simgear;
+
+#define COMPARE(a, b) \
+    if ((a) != (b))  { \
+        cerr << "failed:" << #a << " != " << #b << endl; \
+        cerr << "\tgot:'" << a << "'" << endl; \
+        exit(1); \
+    }
+
+
+int main(int argc, char* argv[])
+{
+    sglog().setLogLevels( SG_ALL, SG_DEBUG );
+
+    DNS::Client cl;
+
+    // test existing NAPTR
+    // fgtest.t3r.de.          600     IN      NAPTR   999 99 "U" "test" "!^.*$!http://dnstest.flightgear.org/!" .
+    {
+       DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest("fgtest.t3r.de");
+       DNS::Request_ptr r(naptrRequest);
+       cl.makeRequest(r);
+       while( !r->complete() )
+               cl.update(0);
+
+       COMPARE(naptrRequest->entries.size(), 1 );
+       COMPARE(naptrRequest->entries[0]->order, 999 );
+       COMPARE(naptrRequest->entries[0]->preference, 99 );
+       COMPARE(naptrRequest->entries[0]->service, "test" );
+       COMPARE(naptrRequest->entries[0]->regexp, "!^.*$!http://dnstest.flightgear.org/!" );
+       COMPARE(naptrRequest->entries[0]->replacement, "" );
+    }
+
+    // test non-existing NAPTR
+    {
+       DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest("jurkxkqdiufqzpfvzqok.prozhqrlcaavbxifkkhf");
+       DNS::Request_ptr r(naptrRequest);
+       cl.makeRequest(r);
+       while( !r->complete() )
+               cl.update(0);
+
+       COMPARE(naptrRequest->entries.size(), 0 );
+    }
+
+    cout << "all tests passed ok" << endl;
+    return EXIT_SUCCESS;
+}
diff --git a/simgear/io/test_DNS.hxx b/simgear/io/test_DNS.hxx
new file mode 100644 (file)
index 0000000..8fd297b
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef SIMGEAR_IO_TEST_DNS_HXX
+#define SIMGEAR_IO_TEST_DNS_HXX
+
+#include <sstream>
+
+#include <simgear/io/sg_netChat.hxx>
+#include <simgear/misc/strutils.hxx>
+
+namespace simgear
+{
+} // of namespace simgear
+
+#endif // of SIMGEAR_IO_TEST_DNS_HXX
diff --git a/simgear/io/text_DNS.cxx b/simgear/io/text_DNS.cxx
new file mode 100644 (file)
index 0000000..44a6c63
--- /dev/null
@@ -0,0 +1,801 @@
+#include <cstdlib>
+
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <errno.h>
+
+#include <boost/algorithm/string/case_conv.hpp>
+
+#include <simgear/simgear_config.h>
+
+#include "HTTPClient.hxx"
+#include "HTTPRequest.hxx"
+
+#include "test_HTTP.hxx"
+
+#include <simgear/misc/strutils.hxx>
+#include <simgear/timing/timestamp.hxx>
+#include <simgear/debug/logstream.hxx>
+
+#if defined(ENABLE_CURL)
+#include <curl/multi.h>
+#endif
+
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::string;
+using std::stringstream;
+
+using namespace simgear;
+
+const char* BODY1 = "The quick brown fox jumps over a lazy dog.";
+const char* BODY3 = "Cras ut neque nulla. Duis ut velit neque, sit amet "
+"pharetra risus. In est ligula, lacinia vitae congue in, sollicitudin at "
+"libero. Mauris pharetra pretium elit, nec placerat dui semper et. Maecenas "
+"magna magna, placerat sed luctus ac, commodo et ligula. Mauris at purus et "
+"nisl molestie auctor placerat at quam. Donec sapien magna, venenatis sed "
+"iaculis id, fringilla vel arcu. Duis sed neque nisi. Cras a arcu sit amet "
+"risus ultrices varius. Integer sagittis euismod dui id varius. Cras vel "
+"justo gravida metus.";
+
+const unsigned int body2Size = 8 * 1024;
+char body2[body2Size];
+
+#define COMPARE(a, b) \
+    if ((a) != (b))  { \
+        cerr << "failed:" << #a << " != " << #b << endl; \
+        cerr << "\tgot:'" << a << "'" << endl; \
+        exit(1); \
+    }
+
+#define VERIFY(a) \
+    if (!(a))  { \
+        cerr << "failed:" << #a << endl; \
+        exit(1); \
+    }
+
+class TestRequest : public HTTP::Request
+{
+public:
+    bool complete;
+    bool failed;
+    string bodyData;
+
+    TestRequest(const std::string& url, const std::string method = "GET") :
+        HTTP::Request(url, method),
+        complete(false),
+        failed(false)
+    {
+
+    }
+
+    std::map<string, string> headers;
+protected:
+
+    virtual void onDone()
+    {
+        complete = true;
+    }
+
+    virtual void onFail()
+    {
+        failed = true;
+    }
+
+    virtual void gotBodyData(const char* s, int n)
+    {
+        //std::cout << "got body data:'" << string(s, n) << "'" <<std::endl;
+        bodyData += string(s, n);
+    }
+
+    virtual void responseHeader(const string& header, const string& value)
+    {
+        Request::responseHeader(header, value);
+        headers[header] =  value;
+    }
+};
+
+class HTTPTestChannel : public TestServerChannel
+{
+public:
+
+    virtual void processRequestHeaders()
+    {
+        if (path == "/test1") {
+            string contentStr(BODY1);
+            stringstream d;
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Content-Length:" << contentStr.size() << "\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+        } else if (path == "/testLorem") {
+            string contentStr(BODY3);
+            stringstream d;
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Content-Length:" << contentStr.size() << "\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+        } else if (path == "/test_zero_length_content") {
+            string contentStr;
+            stringstream d;
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Content-Length:" << contentStr.size() << "\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+        } else if (path == "/test_headers") {
+            COMPARE(requestHeaders["X-Foo"], string("Bar"));
+            COMPARE(requestHeaders["X-AnotherHeader"], string("A longer value"));
+
+            string contentStr(BODY1);
+            stringstream d;
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Content-Length:" << contentStr.size() << "\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+        } else if (path == "/test2") {
+            sendBody2();
+        } else if (path == "/testchunked") {
+            stringstream d;
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Transfer-Encoding:chunked\r\n";
+            d << "\r\n";
+            d << "8\r\n"; // first chunk
+            d << "ABCDEFGH\r\n";
+            d << "6\r\n"; // second chunk
+            d << "ABCDEF\r\n";
+            d << "10\r\n"; // third chunk
+            d << "ABCDSTUVABCDSTUV\r\n";
+            d << "0\r\n"; // start of trailer
+            d << "X-Foobar: wibble\r\n"; // trailer data
+            d << "\r\n";
+            push(d.str().c_str());
+        } else if (path == "http://www.google.com/test2") {
+            // proxy test
+            if (requestHeaders["Host"] != "www.google.com") {
+                sendErrorResponse(400, true, "bad destination");
+            }
+
+            if (requestHeaders["Proxy-Authorization"] != string()) {
+                sendErrorResponse(401, false, "bad auth, not empty"); // shouldn't supply auth
+            }
+
+            sendBody2();
+        } else if (path == "http://www.google.com/test3") {
+            // proxy test
+            if (requestHeaders["Host"] != "www.google.com") {
+                sendErrorResponse(400, true, "bad destination");
+            }
+
+            string credentials = requestHeaders["Proxy-Authorization"];
+            if (credentials.substr(0, 5) != "Basic") {
+              // request basic auth
+              stringstream d;
+              d << "HTTP/1.1 " << 407 << " " << reasonForCode(407) << "\r\n";
+              d << "WWW-Authenticate: Basic real=\"simgear\"\r\n";
+              d << "\r\n"; // final CRLF to terminate the headers
+              push(d.str().c_str());
+              return;
+            }
+
+            std::vector<unsigned char> userAndPass;
+            strutils::decodeBase64(credentials.substr(6), userAndPass);
+            std::string decodedUserPass((char*) userAndPass.data(), userAndPass.size());
+
+            if (decodedUserPass != "johndoe:swordfish") {
+                std::map<string, string>::const_iterator it;
+                for (it = requestHeaders.begin(); it != requestHeaders.end(); ++it) {
+                  cerr << "header:" << it->first << " = " << it->second << endl;
+                }
+
+                sendErrorResponse(401, false, "bad auth, not as set"); // forbidden
+            }
+
+            sendBody2();
+        } else if (strutils::starts_with(path, "/test_1_0")) {
+            string contentStr(BODY1);
+            if (strutils::ends_with(path, "/B")) {
+                contentStr = BODY3;
+            }
+            stringstream d;
+            d << "HTTP/1.0 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+            closeAfterSending();
+        } else if (path == "/test_close") {
+            string contentStr(BODY1);
+            stringstream d;
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Connection: close\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+            closeAfterSending();
+        } else if (path == "/test_abrupt_close") {
+            // simulate server doing socket close before sending any
+            // response - this used to cause a TerraSync failure since we
+            // would get stuck restarting the request
+            closeAfterSending();
+
+        } else if (path == "/test_args") {
+            if ((args["foo"] != "abc") || (args["bar"] != "1234") || (args["username"] != "johndoe")) {
+                sendErrorResponse(400, true, "bad arguments");
+                return;
+            }
+
+            string contentStr(BODY1);
+            stringstream d;
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Content-Length:" << contentStr.size() << "\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+        } else if (path == "/test_post") {
+            if (requestHeaders["Content-Type"] != "application/x-www-form-urlencoded") {
+                cerr << "bad content type: '" << requestHeaders["Content-Type"] << "'" << endl;
+                 sendErrorResponse(400, true, "bad content type");
+                 return;
+            }
+
+            requestContentLength = strutils::to_int(requestHeaders["Content-Length"]);
+            setByteCount(requestContentLength);
+            state = STATE_REQUEST_BODY;
+        } else if ((path == "/test_put") || (path == "/test_create")) {
+              if (requestHeaders["Content-Type"] != "x-application/foobar") {
+                  cerr << "bad content type: '" << requestHeaders["Content-Type"] << "'" << endl;
+                   sendErrorResponse(400, true, "bad content type");
+                   return;
+              }
+
+              requestContentLength = strutils::to_int(requestHeaders["Content-Length"]);
+              setByteCount(requestContentLength);
+              state = STATE_REQUEST_BODY;
+        } else if (path == "/test_get_during_send") {
+            // indicate we will send some number of bytes, but only send
+            // some of them now.
+            waitingOnNextRequestToContinue = true;
+
+            string contentStr(BODY3, 100); // only send first 100 chars
+            stringstream d;
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Content-Length:" << strlen(BODY3) << "\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+        } else if (path == "/test_get_during_send_2") {
+            stringstream d;
+
+            if (waitingOnNextRequestToContinue) {
+                waitingOnNextRequestToContinue = false;
+                // push the rest of the first request
+                d << string(BODY3).substr(100);
+            }
+
+
+            // push the response to this request
+            string contentStr(BODY1);
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Content-Length:" << contentStr.size() << "\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+
+        } else {
+          TestServerChannel::processRequestHeaders();
+        }
+    }
+
+    void processRequestBody()
+    {
+      if (path == "/test_post") {
+            if ((args["foo"] != "abc") || (args["bar"] != "1234") || (args["username"] != "johndoe")) {
+                sendErrorResponse(400, true, "bad arguments");
+                return;
+            }
+
+            stringstream d;
+            d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            push(d.str().c_str());
+        } else if (path == "/test_put") {
+          std::cerr << "sending PUT response" << std::endl;
+
+          COMPARE(buffer, BODY3);
+          stringstream d;
+          d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
+          d << "\r\n"; // final CRLF to terminate the headers
+          push(d.str().c_str());
+        } else if (path == "/test_create") {
+          std::cerr << "sending create response" << std::endl;
+
+          std::string entityStr = "http://localhost:2000/something.txt";
+
+          COMPARE(buffer, BODY3);
+          stringstream d;
+          d << "HTTP/1.1 " << 201 << " " << reasonForCode(201) << "\r\n";
+          d << "Location:" << entityStr << "\r\n";
+          d << "Content-Length:" << entityStr.size() << "\r\n";
+          d << "\r\n"; // final CRLF to terminate the headers
+          d << entityStr;
+
+          push(d.str().c_str());
+        } else {
+          TestServerChannel::processRequestBody();
+        }
+    }
+
+    void sendBody2()
+    {
+        stringstream d;
+        d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+        d << "Content-Length:" << body2Size << "\r\n";
+        d << "\r\n"; // final CRLF to terminate the headers
+        push(d.str().c_str());
+        bufferSend(body2, body2Size);
+    }
+
+    bool waitingOnNextRequestToContinue;
+};
+
+TestServer<HTTPTestChannel> testServer;
+
+void waitForComplete(HTTP::Client* cl, TestRequest* tr)
+{
+    SGTimeStamp start(SGTimeStamp::now());
+    while (start.elapsedMSec() <  10000) {
+        cl->update();
+        testServer.poll();
+
+        if (tr->complete) {
+            return;
+        }
+        SGTimeStamp::sleepForMSec(15);
+    }
+
+    cerr << "timed out" << endl;
+}
+
+void waitForFailed(HTTP::Client* cl, TestRequest* tr)
+{
+    SGTimeStamp start(SGTimeStamp::now());
+    while (start.elapsedMSec() <  10000) {
+        cl->update();
+        testServer.poll();
+
+        if (tr->failed) {
+            return;
+        }
+        SGTimeStamp::sleepForMSec(15);
+    }
+
+    cerr << "timed out waiting for failure" << endl;
+}
+
+int main(int argc, char* argv[])
+{
+    sglog().setLogLevels( SG_ALL, SG_INFO );
+
+    HTTP::Client cl;
+    // force all requests to use the same connection for this test
+    cl.setMaxConnections(1);
+
+// test URL parsing
+    TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
+    COMPARE(tr1->scheme(), "http");
+    COMPARE(tr1->hostAndPort(), "localhost.woo.zar:2000");
+    COMPARE(tr1->host(), "localhost.woo.zar");
+    COMPARE(tr1->port(), 2000);
+    COMPARE(tr1->path(), "/test1");
+
+    TestRequest* tr2 = new TestRequest("http://192.168.1.1/test1/dir/thing/file.png");
+    COMPARE(tr2->scheme(), "http");
+    COMPARE(tr2->hostAndPort(), "192.168.1.1");
+    COMPARE(tr2->host(), "192.168.1.1");
+    COMPARE(tr2->port(), 80);
+    COMPARE(tr2->path(), "/test1/dir/thing/file.png");
+
+// basic get request
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/test1");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseReason(), string("OK"));
+        COMPARE(tr->responseLength(), strlen(BODY1));
+        COMPARE(tr->responseBytesReceived(), strlen(BODY1));
+        COMPARE(tr->bodyData, string(BODY1));
+    }
+
+    {
+      TestRequest* tr = new TestRequest("http://localhost:2000/testLorem");
+      HTTP::Request_ptr own(tr);
+      cl.makeRequest(tr);
+
+      waitForComplete(&cl, tr);
+      COMPARE(tr->responseCode(), 200);
+      COMPARE(tr->responseReason(), string("OK"));
+      COMPARE(tr->responseLength(), strlen(BODY3));
+      COMPARE(tr->responseBytesReceived(), strlen(BODY3));
+      COMPARE(tr->bodyData, string(BODY3));
+    }
+
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_args?foo=abc&bar=1234&username=johndoe");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+    }
+
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_headers");
+        HTTP::Request_ptr own(tr);
+        tr->requestHeader("X-Foo") = "Bar";
+        tr->requestHeader("X-AnotherHeader") = "A longer value";
+        cl.makeRequest(tr);
+
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseReason(), string("OK"));
+        COMPARE(tr->responseLength(), strlen(BODY1));
+        COMPARE(tr->responseBytesReceived(), strlen(BODY1));
+        COMPARE(tr->bodyData, string(BODY1));
+    }
+
+// larger get request
+    for (unsigned int i=0; i<body2Size; ++i) {
+        body2[i] = (i << 4) | (i >> 2);
+    }
+
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/test2");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseBytesReceived(), body2Size);
+        COMPARE(tr->bodyData, string(body2, body2Size));
+    }
+
+    cerr << "testing chunked transfer encoding" << endl;
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/testchunked");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseReason(), string("OK"));
+        COMPARE(tr->responseBytesReceived(), 30);
+        COMPARE(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
+    // check trailers made it too
+        COMPARE(tr->headers["x-foobar"], string("wibble"));
+    }
+
+// test 404
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/not-found");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 404);
+        COMPARE(tr->responseReason(), string("not found"));
+        COMPARE(tr->responseLength(), 0);
+    }
+
+    cout << "done 404 test" << endl;
+
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_args?foo=abc&bar=1234&username=johndoe");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+    }
+
+    cout << "done1" << endl;
+// test HTTP/1.0
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseLength(), strlen(BODY1));
+        COMPARE(tr->bodyData, string(BODY1));
+    }
+
+    cout << "done2" << endl;
+// test HTTP/1.1 Connection::close
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_close");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseLength(), strlen(BODY1));
+        COMPARE(tr->bodyData, string(BODY1));
+    }
+    cout << "done3" << endl;
+// test connectToHost failure
+
+    {
+        TestRequest* tr = new TestRequest("http://not.found/something");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForFailed(&cl, tr);
+
+
+
+#if defined(ENABLE_CURL)
+      const int HOST_NOT_FOUND_CODE = CURLE_COULDNT_RESOLVE_HOST;
+#else
+      const int HOST_NOT_FOUND_CODE = ENOENT;
+#endif
+        COMPARE(tr->responseCode(), HOST_NOT_FOUND_CODE);
+    }
+
+  cout << "testing abrupt close" << endl;
+    // test server-side abrupt close
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_abrupt_close");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForFailed(&cl, tr);
+
+  #if defined(ENABLE_CURL)
+        const int SERVER_NO_DATA_CODE = CURLE_GOT_NOTHING;
+  #else
+        const int SERVER_NO_DATA_CODE = 500;
+  #endif
+        COMPARE(tr->responseCode(), SERVER_NO_DATA_CODE);
+    }
+
+cout << "testing proxy close" << endl;
+// test proxy
+    {
+        cl.setProxy("localhost", 2000);
+        TestRequest* tr = new TestRequest("http://www.google.com/test2");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseLength(), body2Size);
+        COMPARE(tr->bodyData, string(body2, body2Size));
+    }
+
+#if defined(ENABLE_CURL)
+    {
+        cl.setProxy("localhost", 2000, "johndoe:swordfish");
+        TestRequest* tr = new TestRequest("http://www.google.com/test3");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseBytesReceived(), body2Size);
+        COMPARE(tr->bodyData, string(body2, body2Size));
+    }
+#endif
+
+// pipelining
+    cout << "testing HTTP 1.1 pipelining" << endl;
+
+    {
+        testServer.resetConnectCount();
+        cl.clearAllConnections();
+        
+        cl.setProxy("", 80);
+        TestRequest* tr = new TestRequest("http://localhost:2000/test1");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+
+
+        TestRequest* tr2 = new TestRequest("http://localhost:2000/testLorem");
+        HTTP::Request_ptr own2(tr2);
+        cl.makeRequest(tr2);
+
+        TestRequest* tr3 = new TestRequest("http://localhost:2000/test1");
+        HTTP::Request_ptr own3(tr3);
+        cl.makeRequest(tr3);
+
+        waitForComplete(&cl, tr3);
+        VERIFY(tr->complete);
+        VERIFY(tr2->complete);
+        COMPARE(tr->bodyData, string(BODY1));
+
+        COMPARE(tr2->responseLength(), strlen(BODY3));
+        COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
+        COMPARE(tr2->bodyData, string(BODY3));
+
+        COMPARE(tr3->bodyData, string(BODY1));
+
+        COMPARE(testServer.connectCount(), 1);
+    }
+
+// multiple requests with an HTTP 1.0 server
+    {
+        cout << "http 1.0 multiple requests" << endl;
+
+        cl.setProxy("", 80);
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0/A");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+
+        TestRequest* tr2 = new TestRequest("http://localhost:2000/test_1_0/B");
+        HTTP::Request_ptr own2(tr2);
+        cl.makeRequest(tr2);
+
+        TestRequest* tr3 = new TestRequest("http://localhost:2000/test_1_0/C");
+        HTTP::Request_ptr own3(tr3);
+        cl.makeRequest(tr3);
+
+        waitForComplete(&cl, tr3);
+        VERIFY(tr->complete);
+        VERIFY(tr2->complete);
+
+        COMPARE(tr->responseLength(), strlen(BODY1));
+        COMPARE(tr->responseBytesReceived(), strlen(BODY1));
+        COMPARE(tr->bodyData, string(BODY1));
+
+        COMPARE(tr2->responseLength(), strlen(BODY3));
+        COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
+        COMPARE(tr2->bodyData, string(BODY3));
+        COMPARE(tr3->bodyData, string(BODY1));
+    }
+
+// POST
+    {
+        cout << "testing POST" << endl;
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_post?foo=abc&bar=1234&username=johndoe", "POST");
+        tr->setBodyData("", "application/x-www-form-urlencoded");
+
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 204);
+    }
+
+    // PUT
+        {
+            cout << "testing PUT" << endl;
+            TestRequest* tr = new TestRequest("http://localhost:2000/test_put", "PUT");
+            tr->setBodyData(BODY3, "x-application/foobar");
+
+            HTTP::Request_ptr own(tr);
+            cl.makeRequest(tr);
+            waitForComplete(&cl, tr);
+            COMPARE(tr->responseCode(), 204);
+        }
+
+        {
+            cout << "testing PUT create" << endl;
+            TestRequest* tr = new TestRequest("http://localhost:2000/test_create", "PUT");
+            tr->setBodyData(BODY3, "x-application/foobar");
+
+            HTTP::Request_ptr own(tr);
+            cl.makeRequest(tr);
+            waitForComplete(&cl, tr);
+            COMPARE(tr->responseCode(), 201);
+        }
+
+    // test_zero_length_content
+    {
+        cout << "zero-length-content-response" << endl;
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_zero_length_content");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->bodyData, string());
+        COMPARE(tr->responseBytesReceived(), 0);
+    }
+
+    // test cancel
+    {
+        cout <<  "cancel  request" << endl;
+        testServer.resetConnectCount();
+        cl.clearAllConnections();
+
+        cl.setProxy("", 80);
+        TestRequest* tr = new TestRequest("http://localhost:2000/test1");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+
+        TestRequest* tr2 = new TestRequest("http://localhost:2000/testLorem");
+        HTTP::Request_ptr own2(tr2);
+        cl.makeRequest(tr2);
+
+        TestRequest* tr3 = new TestRequest("http://localhost:2000/test1");
+        HTTP::Request_ptr own3(tr3);
+        cl.makeRequest(tr3);
+
+        cl.cancelRequest(tr, "my reason 1");
+
+        cl.cancelRequest(tr2, "my reason 2");
+
+        waitForComplete(&cl, tr3);
+
+        COMPARE(tr->responseCode(), -1);
+        COMPARE(tr2->responseReason(), "my reason 2");
+
+        COMPARE(tr3->responseLength(), strlen(BODY1));
+        COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
+        COMPARE(tr3->bodyData, string(BODY1));
+    }
+
+    // test cancel
+    {
+        cout <<  "cancel middle request" << endl;
+        testServer.resetConnectCount();
+        cl.clearAllConnections();
+
+        cl.setProxy("", 80);
+        TestRequest* tr = new TestRequest("http://localhost:2000/test1");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+
+        TestRequest* tr2 = new TestRequest("http://localhost:2000/testLorem");
+        HTTP::Request_ptr own2(tr2);
+        cl.makeRequest(tr2);
+
+        TestRequest* tr3 = new TestRequest("http://localhost:2000/test1");
+        HTTP::Request_ptr own3(tr3);
+        cl.makeRequest(tr3);
+
+        cl.cancelRequest(tr2, "middle request");
+
+        waitForComplete(&cl, tr3);
+
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseLength(), strlen(BODY1));
+        COMPARE(tr->responseBytesReceived(), strlen(BODY1));
+        COMPARE(tr->bodyData, string(BODY1));
+
+        COMPARE(tr2->responseCode(), -1);
+
+        COMPARE(tr3->responseLength(), strlen(BODY1));
+        COMPARE(tr3->responseBytesReceived(), strlen(BODY1));
+        COMPARE(tr3->bodyData, string(BODY1));
+    }
+
+    {
+        cout << "get-during-response-send" << endl;
+        cl.clearAllConnections();
+        //test_get_during_send
+
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_get_during_send");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+
+        // kick things along
+        for (int i=0; i<10; ++i) {
+            SGTimeStamp::sleepForMSec(1);
+            cl.update();
+            testServer.poll();
+
+        }
+
+        TestRequest* tr2 = new TestRequest("http://localhost:2000/test_get_during_send_2");
+        HTTP::Request_ptr own2(tr2);
+        cl.makeRequest(tr2);
+
+        waitForComplete(&cl, tr2);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->bodyData, string(BODY3));
+        COMPARE(tr->responseBytesReceived(), strlen(BODY3));
+        COMPARE(tr2->responseCode(), 200);
+        COMPARE(tr2->bodyData, string(BODY1));
+        COMPARE(tr2->responseBytesReceived(), strlen(BODY1));
+    }
+
+    cout << "all tests passed ok" << endl;
+    return EXIT_SUCCESS;
+}