diff --git a/.gitmodules b/.gitmodules index c46b1c736fc..7d0ca61ebe2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -274,3 +274,9 @@ [submodule "contrib/base-x"] path = contrib/base-x url = https://github.com/ClickHouse/base-x.git +[submodule "contrib/cpp-dns"] + path = contrib/cpp-dns + url = git@github.com:YukiWorkshop/cpp-dns.git +[submodule "contrib/udns"] + path = contrib/udns + url = https://github.com/arthurpassos/udns.git \ No newline at end of file diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 1dd28fa90ff..06e90dedb5b 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -156,6 +156,8 @@ endif() add_contrib (sqlite-cmake sqlite-amalgamation) add_contrib (s2geometry-cmake s2geometry) add_contrib (base-x-cmake base-x) +add_contrib (udns-cmake udns) +add_contrib (cpp-dns-cmake cpp-dns) # Put all targets defined here and in subdirectories under "contrib/" folders in GUI-based IDEs. # Some of third-party projects may override CMAKE_FOLDER or FOLDER property of their targets, so they would not appear diff --git a/contrib/cpp-dns b/contrib/cpp-dns new file mode 160000 index 00000000000..0ce2dbd40dc --- /dev/null +++ b/contrib/cpp-dns @@ -0,0 +1 @@ +Subproject commit 0ce2dbd40dce456bde1732c4d8f70008f0079db0 diff --git a/contrib/cpp-dns-cmake/CMakeLists.txt b/contrib/cpp-dns-cmake/CMakeLists.txt new file mode 100644 index 00000000000..342e189ff36 --- /dev/null +++ b/contrib/cpp-dns-cmake/CMakeLists.txt @@ -0,0 +1,31 @@ +set(LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/cpp-dns") + +# If not, ASIO standalone mode. +#find_path(LIBUDNS_INCLUDE_DIR +# NAME udns.h +# HINTS /opt/local/include) +# +#find_library(lib_udns udns +# PATHS /opt/local/lib) + +#if (lib_udns) +# message("Found libudns!") +#else() +# message(FATAL_ERROR "udns not found") +#endif() + +#find_package(Boost 1.78 COMPONENTS system REQUIRED)#[[]] + +set(SRCS "${LIBRARY_DIR}/DNSResolver.cpp") + +set(HDRS "${LIBRARY_DIR}/DNSResolver.hpp") + +add_library(_cpp-dns ${SRCS} ${HDRS}) + +#message("Libraries: lb: ${LIBRARY_DIR} bid: ${Boost_INCLUDE_DIRS} bl: ${Boost_LIBRARIES} libudns: ${lib_udns}") + +target_link_libraries(_cpp-dns boost::system boost::headers_only ch_contrib::udns) + +target_include_directories(_cpp-dns SYSTEM BEFORE PUBLIC ${LIBRARY_DIR}) + +add_library(ch_contrib::cpp-dns ALIAS _cpp-dns) \ No newline at end of file diff --git a/contrib/udns-cmake/CMakeLists.txt b/contrib/udns-cmake/CMakeLists.txt new file mode 100644 index 00000000000..d56be90e11f --- /dev/null +++ b/contrib/udns-cmake/CMakeLists.txt @@ -0,0 +1,54 @@ +set(LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/udns") + +#add_library(udns +# 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 +# ) + +#add_library(_udns +# "${LIBRARY_DIR}/udns_dn.c" +# "${LIBRARY_DIR}/dnsget.c" "${LIBRARY_DIR}/ex-rdns.c" "${LIBRARY_DIR}/getopt.c" "${LIBRARY_DIR}/inet_XtoX.c" +# "${LIBRARY_DIR}/rblcheck.c" "${LIBRARY_DIR}/udns_bl.c" "${LIBRARY_DIR}/udns_dntosp.c" +# "${LIBRARY_DIR}/udns_init.c" "${LIBRARY_DIR}/udns_jran.c" "${LIBRARY_DIR}/udns_misc.c" "${LIBRARY_DIR}/udns_parse.c" +# "${LIBRARY_DIR}/udns_resolver.c" "${LIBRARY_DIR}/udns_rr_a.c" "${LIBRARY_DIR}/udns_rr_mx.c" +# "${LIBRARY_DIR}/udns_rr_naptr.c" "${LIBRARY_DIR}/udns_rr_ptr.c" "${LIBRARY_DIR}/udns_rr_srv.c" "${LIBRARY_DIR}/udns_rr_txt.c" +# "${LIBRARY_DIR}/udns_XtoX.c" ) + +add_library(_udns + "${LIBRARY_DIR}/udns_dn.c" + "${LIBRARY_DIR}/udns_dntosp.c" + "${LIBRARY_DIR}/udns_parse.c" + "${LIBRARY_DIR}/udns_resolver.c" + "${LIBRARY_DIR}/udns_init.c" + "${LIBRARY_DIR}/udns_misc.c" + "${LIBRARY_DIR}/udns_XtoX.c" + "${LIBRARY_DIR}/udns_rr_a.c" + "${LIBRARY_DIR}/udns_rr_ptr.c" + "${LIBRARY_DIR}/udns_rr_mx.c" + "${LIBRARY_DIR}/udns_rr_txt.c" + "${LIBRARY_DIR}/udns_bl.c" + "${LIBRARY_DIR}/udns_rr_srv.c" + "${LIBRARY_DIR}/udns_rr_naptr.c" + "${LIBRARY_DIR}/udns_codes.c" + "${LIBRARY_DIR}/udns_jran.c" + ) + +target_compile_definitions(_udns PRIVATE -DHAVE_CONFIG_H) + +target_include_directories(_udns SYSTEM BEFORE PUBLIC ${LIBRARY_DIR}) + +add_library(ch_contrib::udns ALIAS _udns) \ No newline at end of file diff --git a/contrib/udns/COPYING.LGPL b/contrib/udns/COPYING.LGPL new file mode 100644 index 00000000000..4362b49151d --- /dev/null +++ b/contrib/udns/COPYING.LGPL @@ -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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/contrib/udns/Makefile.in b/contrib/udns/Makefile.in new file mode 100644 index 00000000000..ec085206655 --- /dev/null +++ b/contrib/udns/Makefile.in @@ -0,0 +1,197 @@ +#! /usr/bin/make -rf +# Makefile.in +# libudns Makefile +# +# Copyright (C) 2005 Michael Tokarev +# 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/contrib/udns/NEWS b/contrib/udns/NEWS new file mode 100644 index 00000000000..88aff6fa420 --- /dev/null +++ b/contrib/udns/NEWS @@ -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/contrib/udns/NOTES b/contrib/udns/NOTES new file mode 100644 index 00000000000..b99a077bb22 --- /dev/null +++ b/contrib/udns/NOTES @@ -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/contrib/udns/README.md b/contrib/udns/README.md new file mode 100644 index 00000000000..f5fcc68aa79 --- /dev/null +++ b/contrib/udns/README.md @@ -0,0 +1 @@ +# udns diff --git a/contrib/udns/TODO b/contrib/udns/TODO new file mode 100644 index 00000000000..0dc9b967c60 --- /dev/null +++ b/contrib/udns/TODO @@ -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/contrib/udns/config.h b/contrib/udns/config.h new file mode 100644 index 00000000000..5d79367247f --- /dev/null +++ b/contrib/udns/config.h @@ -0,0 +1,7 @@ +#pragma once +/* automatically generated by configure. */ + +#define HAVE_GETOPT 1 +#define HAVE_INET_PTON_NTOP 1 +#define HAVE_IPv6 1 +#define HAVE_POLL 1 \ No newline at end of file diff --git a/contrib/udns/configure b/contrib/udns/configure new file mode 100755 index 00000000000..a3c0a0dd3d0 --- /dev/null +++ b/contrib/udns/configure @@ -0,0 +1,166 @@ +#! /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 <&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 < +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" < +int main() { int socket_fd = socket(); connect(socket_fd); 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 < +#include +#include +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 < +#include +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 < +#include +#include +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 < +#include +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 < +#include +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/contrib/udns/configure.lib b/contrib/udns/configure.lib new file mode 100644 index 00000000000..541177a095b --- /dev/null +++ b/contrib/udns/configure.lib @@ -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 <&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 <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/contrib/udns/conftest.c b/contrib/udns/conftest.c new file mode 100644 index 00000000000..5bc7720cbaa --- /dev/null +++ b/contrib/udns/conftest.c @@ -0,0 +1,3 @@ +#include + +int main() { socket(); connect(); return 0; } diff --git a/contrib/udns/conftest.out b/contrib/udns/conftest.out new file mode 100644 index 00000000000..cfff425825a --- /dev/null +++ b/contrib/udns/conftest.out @@ -0,0 +1,7 @@ +conftest.c:1:14: error: implicit declaration of function 'socket' is invalid in C99 [-Werror,-Wimplicit-function-declaration] +int main() { socket(); connect(); return 0; } + ^ +conftest.c:1:24: error: implicit declaration of function 'connect' is invalid in C99 [-Werror,-Wimplicit-function-declaration] +int main() { socket(); connect(); return 0; } + ^ +2 errors generated. diff --git a/contrib/udns/dnsget.1 b/contrib/udns/dnsget.1 new file mode 100644 index 00000000000..200557fe947 --- /dev/null +++ b/contrib/udns/dnsget.1 @@ -0,0 +1,195 @@ +.\" dnsget.1: dnsget manpage +.\" +.\" Copyright (C) 2005-2014 Michael Tokarev +.\" 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/contrib/udns/dnsget.c b/contrib/udns/dnsget.c new file mode 100644 index 00000000000..417e8d9743c --- /dev/null +++ b/contrib/udns/dnsget.c @@ -0,0 +1,759 @@ +/* dnsget.c + simple host/dig-like application using UDNS library + + Copyright (C) 2005 Michael Tokarev + 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 +#include +#else +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#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("\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("<>\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("<>\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/contrib/udns/ex-rdns.c b/contrib/udns/ex-rdns.c new file mode 100644 index 00000000000..1e1e90d4b1d --- /dev/null +++ b/contrib/udns/ex-rdns.c @@ -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 + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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/contrib/udns/getopt.c b/contrib/udns/getopt.c new file mode 100644 index 00000000000..e15a7a0579e --- /dev/null +++ b/contrib/udns/getopt.c @@ -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 + +#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 +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 + +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/contrib/udns/inet_XtoX.c b/contrib/udns/inet_XtoX.c new file mode 100644 index 00000000000..50b5f8e81f3 --- /dev/null +++ b/contrib/udns/inet_XtoX.c @@ -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 + +#ifdef TEST + +# include +# include +# include +# include +# include +# include +# include +# 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/contrib/udns/rblcheck.1 b/contrib/udns/rblcheck.1 new file mode 100644 index 00000000000..6c822c01c0b --- /dev/null +++ b/contrib/udns/rblcheck.1 @@ -0,0 +1,151 @@ +.\" rblcheck.1 +.\" rblckeck manpage +.\" +.\" Copyright (C) 2005 Michael Tokarev +.\" 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/contrib/udns/rblcheck.c b/contrib/udns/rblcheck.c new file mode 100644 index 00000000000..82d29deeebf --- /dev/null +++ b/contrib/udns/rblcheck.c @@ -0,0 +1,378 @@ +/* rblcheck.c + dnsbl (rbl) checker application + + Copyright (C) 2005 Michael Tokarev + 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 +#include +#include +#ifdef WINDOWS +# include +#else +# include +# include +# include +# include +#endif +#include +#include +#include +#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", 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/contrib/udns/udns.3 b/contrib/udns/udns.3 new file mode 100644 index 00000000000..23222aae9f7 --- /dev/null +++ b/contrib/udns/udns.3 @@ -0,0 +1,1352 @@ +.\" udns.3 +.\" udns library manpage +.\" +.\" Copyright (C) 2005-2014 Michael Tokarev +.\" 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 +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/contrib/udns/udns.h b/contrib/udns/udns.h new file mode 100644 index 00000000000..371e6976404 --- /dev/null +++ b/contrib/udns/udns.h @@ -0,0 +1,778 @@ +/* udns.h + header file for the UDNS library. + + Copyright (C) 2005 Michael Tokarev + 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 /* 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/contrib/udns/udns_XtoX.c b/contrib/udns/udns_XtoX.c new file mode 100644 index 00000000000..60e3fdfa320 --- /dev/null +++ b/contrib/udns/udns_XtoX.c @@ -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 + 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 +#include +#include + +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/contrib/udns/udns_bl.c b/contrib/udns/udns_bl.c new file mode 100644 index 00000000000..f6be39335f1 --- /dev/null +++ b/contrib/udns/udns_bl.c @@ -0,0 +1,160 @@ +/* udns_bl.c + DNSBL stuff + + Copyright (C) 2005 Michael Tokarev + 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/contrib/udns/udns_codes.c b/contrib/udns/udns_codes.c new file mode 100644 index 00000000000..c637e98c581 --- /dev/null +++ b/contrib/udns/udns_codes.c @@ -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/contrib/udns/udns_dn.c b/contrib/udns/udns_dn.c new file mode 100644 index 00000000000..ae3fd177882 --- /dev/null +++ b/contrib/udns/udns_dn.c @@ -0,0 +1,379 @@ +/* udns_dn.c + domain names manipulation routines + + Copyright (C) 2005 Michael Tokarev + 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 +#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 +#include + +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/contrib/udns/udns_dntosp.c b/contrib/udns/udns_dntosp.c new file mode 100644 index 00000000000..823fde211e5 --- /dev/null +++ b/contrib/udns/udns_dntosp.c @@ -0,0 +1,30 @@ +/* udns_dntosp.c + dns_dntosp() = convert DN to asciiz string using static buffer + + Copyright (C) 2005 Michael Tokarev + 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/contrib/udns/udns_init.c b/contrib/udns/udns_init.c new file mode 100644 index 00000000000..493af58917f --- /dev/null +++ b/contrib/udns/udns_init.c @@ -0,0 +1,231 @@ +/* udns_init.c + resolver initialisation stuff + + Copyright (C) 2006 Michael Tokarev + 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 /* includes */ +# include /* for dns server addresses etc */ +#else +# include +# include +# include +#endif /* !WINDOWS */ + +#include +#include +#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/contrib/udns/udns_jran.c b/contrib/udns/udns_jran.c new file mode 100644 index 00000000000..19f9d02bb14 --- /dev/null +++ b/contrib/udns/udns_jran.c @@ -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/contrib/udns/udns_misc.c b/contrib/udns/udns_misc.c new file mode 100644 index 00000000000..c162e70e77b --- /dev/null +++ b/contrib/udns/udns_misc.c @@ -0,0 +1,67 @@ +/* udns_misc.c + miscellaneous routines + + Copyright (C) 2005 Michael Tokarev + 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/contrib/udns/udns_parse.c b/contrib/udns/udns_parse.c new file mode 100644 index 00000000000..8924b1562d2 --- /dev/null +++ b/contrib/udns/udns_parse.c @@ -0,0 +1,169 @@ +/* udns_parse.c + raw DNS packet parsing routines + + Copyright (C) 2005 Michael Tokarev + 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 +#include +#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/contrib/udns/udns_resolver.c b/contrib/udns/udns_resolver.c new file mode 100644 index 00000000000..b8f899a2460 --- /dev/null +++ b/contrib/udns/udns_resolver.c @@ -0,0 +1,1323 @@ +/* udns_resolver.c + resolver stuff (main module) + + Copyright (C) 2005 Michael Tokarev + 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 /* includes */ +# include /* needed for struct in6_addr */ +#else +# include +# include +# include +# include +# include +# include +# ifdef HAVE_POLL +# include +# else +# ifdef HAVE_SYS_SELECT_H +# include +# endif +# endif +# ifdef HAVE_TIMES +# include +# endif +# define closesocket(sock) close(sock) +#endif /* !WINDOWS */ + +#include +#include +#include +#include +#include +#include +#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/contrib/udns/udns_rr_a.c b/contrib/udns/udns_rr_a.c new file mode 100644 index 00000000000..4fdcbf9bc1c --- /dev/null +++ b/contrib/udns/udns_rr_a.c @@ -0,0 +1,123 @@ +/* udns_rr_a.c + parse/query A/AAAA IN records + + Copyright (C) 2005 Michael Tokarev + 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 +#include +#include +#ifndef WINDOWS +# include +# include +#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/contrib/udns/udns_rr_mx.c b/contrib/udns/udns_rr_mx.c new file mode 100644 index 00000000000..0904e017177 --- /dev/null +++ b/contrib/udns/udns_rr_mx.c @@ -0,0 +1,91 @@ +/* udns_rr_mx.c + parse/query MX IN records + + Copyright (C) 2005 Michael Tokarev + 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 +#include +#include +#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/contrib/udns/udns_rr_naptr.c b/contrib/udns/udns_rr_naptr.c new file mode 100644 index 00000000000..da30069db62 --- /dev/null +++ b/contrib/udns/udns_rr_naptr.c @@ -0,0 +1,128 @@ +/* udns_rr_naptr.c + parse/query NAPTR IN records + + Copyright (C) 2005 Michael Tokarev + Copyright (C) 2006 Mikael Magnusson + 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 +#include +#include +#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/contrib/udns/udns_rr_ptr.c b/contrib/udns/udns_rr_ptr.c new file mode 100644 index 00000000000..1f682aebc0f --- /dev/null +++ b/contrib/udns/udns_rr_ptr.c @@ -0,0 +1,109 @@ +/* udns_rr_ptr.c + parse/query PTR records + + Copyright (C) 2005 Michael Tokarev + 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 +#include +#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/contrib/udns/udns_rr_srv.c b/contrib/udns/udns_rr_srv.c new file mode 100644 index 00000000000..dfba4653a3a --- /dev/null +++ b/contrib/udns/udns_rr_srv.c @@ -0,0 +1,155 @@ +/* udns_rr_srv.c + parse/query SRV IN (rfc2782) records + + Copyright (C) 2005 Michael Tokarev + 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 + + 2005-09-11: + Changed MX parser file into a SRV parser file + + */ + +#include +#include +#include +#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/contrib/udns/udns_rr_txt.c b/contrib/udns/udns_rr_txt.c new file mode 100644 index 00000000000..97f1dfb18c3 --- /dev/null +++ b/contrib/udns/udns_rr_txt.c @@ -0,0 +1,98 @@ +/* udns_rr_txt.c + parse/query TXT records + + Copyright (C) 2005 Michael Tokarev + 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 +#include +#include +#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); +} diff --git a/src/Access/Common/AllowedClientHosts.cpp b/src/Access/Common/AllowedClientHosts.cpp index 85d7065d823..09058a3c045 100644 --- a/src/Access/Common/AllowedClientHosts.cpp +++ b/src/Access/Common/AllowedClientHosts.cpp @@ -110,18 +110,23 @@ namespace } /// Returns the host name by its address. - String getHostByAddress(const IPAddress & address) + std::vector getHostsByAddress(const IPAddress & address) { - String host = DNSResolver::instance().reverseResolve(address); + std::vector hosts = DNSResolver::instance().reverseResolve(address); - /// Check that PTR record is resolved back to client address - if (!isAddressOfHost(address, host)) - throw Exception("Host " + String(host) + " isn't resolved back to " + address.toString(), ErrorCodes::DNS_ERROR); + if (hosts.empty()) + throw Exception(address.toString() + " could not be resolved", ErrorCodes::DNS_ERROR); - return host; + + for (auto & host : hosts) { + /// Check that PTR record is resolved back to client address + if (!isAddressOfHost(address, host)) + throw Exception("Host " + String(host) + " isn't resolved back to " + address.toString(), ErrorCodes::DNS_ERROR); + } + + return hosts; } - void parseLikePatternIfIPSubnet(const String & pattern, IPSubnet & subnet, IPAddress::Family address_family) { size_t slash = pattern.find('/'); @@ -520,20 +525,26 @@ bool AllowedClientHosts::contains(const IPAddress & client_address) const return true; /// Check `name_regexps`. - std::optional resolved_host; + std::optional> resolved_hosts; auto check_name_regexp = [&](const String & name_regexp_) { try { if (boost::iequals(name_regexp_, "localhost")) return is_client_local(); - if (!resolved_host) - resolved_host = getHostByAddress(client_v6); - if (resolved_host->empty()) - return false; - Poco::RegularExpression re(name_regexp_); - Poco::RegularExpression::Match match; - return re.match(*resolved_host, match) != 0; + if (!resolved_hosts) { + resolved_hosts = getHostsByAddress(client_address); + } + + for (const auto & host : resolved_hosts.value()) { + Poco::RegularExpression re(name_regexp_); + Poco::RegularExpression::Match match; + if (re.match(host, match) != 0) { + return true; + } + } + + return false; } catch (const Exception & e) { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 10bdc464ac6..6d4e7813bfa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -355,6 +355,8 @@ target_link_libraries(clickhouse_common_io ch_contrib::zlib pcg_random Poco::Foundation + ch_contrib::udns + ch_contrib::cpp-dns ) if (TARGET ch_contrib::cpuid) @@ -439,6 +441,14 @@ if (TARGET ch_contrib::avrocpp) dbms_target_link_libraries(PRIVATE ch_contrib::avrocpp) endif () +if (TARGET ch_contrib::udns) + dbms_target_link_libraries(PRIVATE ch_contrib::udns) +endif () + +if (TARGET ch_contrib::cpp-dns) + dbms_target_link_libraries(PRIVATE ch_contrib::cpp-dns) +endif () + if (TARGET OpenSSL::Crypto) dbms_target_link_libraries (PRIVATE OpenSSL::Crypto) target_link_libraries (clickhouse_common_io PRIVATE OpenSSL::Crypto) diff --git a/src/Common/DNSResolver.cpp b/src/Common/DNSResolver.cpp index 0616e324b73..d5eebe579b4 100644 --- a/src/Common/DNSResolver.cpp +++ b/src/Common/DNSResolver.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include namespace ProfileEvents { @@ -138,16 +140,27 @@ static DNSResolver::IPAddresses resolveIPAddressImpl(const std::string & host) return addresses; } -static String reverseResolveImpl(const Poco::Net::IPAddress & address) +static std::vector reverseResolveImpl(const Poco::Net::IPAddress & address) { - Poco::Net::SocketAddress sock_addr(address, 0); + boost::asio::io_service ioService; + YukiWorkshop::DNSResolver resolver(ioService); - /// Resolve by hand, because Poco::Net::DNS::hostByAddress(...) does getaddrinfo(...) after getnameinfo(...) - char host[1024]; - int err = getnameinfo(sock_addr.addr(), sock_addr.length(), host, sizeof(host), nullptr, 0, NI_NAMEREQD); - if (err) - throw Exception("Cannot getnameinfo(" + address.toString() + "): " + gai_strerror(err), ErrorCodes::DNS_ERROR); - return host; + std::vector ptrRecords; + + resolver.resolve_a4ptr(boost::asio::ip::address_v4::from_string(address.toString()), [&](int err, auto& hosts, auto&, auto&, uint) { + if (err) { + throw Exception("Cannot resolve: " + address.toString() + gai_strerror(err), ErrorCodes::DNS_ERROR); + } + + for (auto &it : hosts) { + ptrRecords.emplace_back(it); + } + + }); + + ioService.run(); + + return ptrRecords; } struct DNSResolver::Impl @@ -235,7 +248,7 @@ std::vector DNSResolver::resolveAddressList(const std: return addresses; } -String DNSResolver::reverseResolve(const Poco::Net::IPAddress & address) +std::vector DNSResolver::reverseResolve(const Poco::Net::IPAddress & address) { if (impl->disable_cache) return reverseResolveImpl(address); diff --git a/src/Common/DNSResolver.h b/src/Common/DNSResolver.h index fdd9799f96f..d1a8843d234 100644 --- a/src/Common/DNSResolver.h +++ b/src/Common/DNSResolver.h @@ -36,8 +36,8 @@ public: std::vector resolveAddressList(const std::string & host, UInt16 port); - /// Accepts host IP and resolves its host name - String reverseResolve(const Poco::Net::IPAddress & address); + /// Accepts host IP and resolves its host names + std::vector reverseResolve(const Poco::Net::IPAddress & address); /// Get this server host name String getHostName();