diff --git a/conf/freeswitch.xml b/conf/freeswitch.xml
index 32b474cb64..ce1570c5b6 100644
--- a/conf/freeswitch.xml
+++ b/conf/freeswitch.xml
@@ -22,6 +22,10 @@
+
+
+
+
@@ -77,7 +81,6 @@
-
@@ -404,6 +407,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -415,7 +431,8 @@
-
+
+
diff --git a/libs/udns/COPYING.LGPL b/libs/udns/COPYING.LGPL
new file mode 100644
index 0000000000..b1e3f5a263
--- /dev/null
+++ b/libs/udns/COPYING.LGPL
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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/libs/udns/Makefile b/libs/udns/Makefile
new file mode 100755
index 0000000000..2fcb20a5fd
--- /dev/null
+++ b/libs/udns/Makefile
@@ -0,0 +1,132 @@
+#! /usr/bin/make -rf
+# $Id: Makefile,v 1.42 2006/11/29 21:27:01 mjt Exp $
+# 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
+
+VERS = 0.0.9pre
+SRCS = udns_dn.c udns_dntosp.c udns_parse.c udns_resolver.c udns_misc.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
+USRCS = dnsget.c rblcheck.c ex-rdns.c
+DEB = debian/copyright debian/changelog debian/control debian/rules
+DIST = COPYING.LGPL udns.h udns.3 dnsget.1 rblcheck.1 $(SRCS) $(USRCS) \
+ Makefile TODO NOTES
+
+OBJS = $(SRCS:.c=.o) $(GEN:.c=.o)
+LIB = libudns.a
+
+SOVER = 0
+SOBJS = $(OBJS:.o=.lo)
+SOLIB = libudns.so
+SOLIBV = $(SOLIB).$(SOVER)
+
+LIBS = $(LIB) $(SOLIBV)
+
+UTILS = $(USRCS:.c=)
+SOUTILS = $(USRCS:.c=.shared)
+
+NAMEPFX = udns-$(VERS)
+
+CC = gcc
+CFLAGS = -Wall -W -Wmissing-prototypes -O2 -fPIC
+CDEFS = -DHAVE_POLL
+PICFLAGS = -fPIC
+AWK = awk
+
+all: static
+
+.SUFFIXES: .c .o .lo .shared
+
+static: $(LIB) $(UTILS)
+staticlib: $(LIB)
+$(LIB): $(OBJS)
+ -rm -f $@
+ $(AR) rv $@ $(OBJS)
+.c.o:
+ $(CC) $(CFLAGS) $(CDEFS) -c $<
+$(OBJS): udns.h
+
+$(UTILS): udns.h $(LIB)
+.c:
+ $(CC) $(CFLAGS) $(CDEFS) -o $@ $< $(LIB)
+
+shared: $(SOLIBV) $(SOUTILS)
+sharedlib: $(SOLIBV)
+
+$(SOLIBV): $(SOBJS)
+ $(CC) -shared -Wl,--soname,$(SOLIBV) -o $@ $(SOBJS)
+ rm -f $(SOLIB)
+ ln -s $(SOLIBV) $(SOLIB)
+.c.lo:
+ $(CC) $(CFLAGS) $(PICFLAGS) $(CDEFS) -o $@ -c $<
+$(SOBJS): udns.h
+
+$(SOUTILS): udns.h $(SOLIB)
+.c.shared:
+ $(CC) $(CFLAGS) $(CDEFS) -o $@ $< $(SOLIB)
+
+# udns_codes.c is generated from udns.h
+udns_codes.c: udns.h Makefile
+ @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) $(DEB)
+ mkdir $(NAMEPFX) $(NAMEPFX)/debian
+ ln $(DIST) $(NAMEPFX)
+ ln $(DEB) $(NAMEPFX)/debian
+ tar cvfz $@ $(NAMEPFX)
+ rm -rf $(NAMEPFX)
+subdist:
+ cp -p $(DIST) $(TARGET)/
+
+clean:
+ rm -f $(OBJS) $(SOBJS) build-stamp
+distclean: clean
+ rm -f $(LIBS) libudns.so udns.3.html $(UTILS) $(SOUTILS)
+
+.PHONY: all static staticlib shared sharedlib dist clean distclean subdist
diff --git a/libs/udns/NOTES b/libs/udns/NOTES
new file mode 100644
index 0000000000..3fe75c1f32
--- /dev/null
+++ b/libs/udns/NOTES
@@ -0,0 +1,193 @@
+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).
+
+ - some DNS servers, notable the ones used by Verisign for certain top-level
+ domains, chokes on EDNS0-enabled queries, returning FORMERR. Such
+ behavior isn't prohibited by DNS standards, but in my opinion it's at
+ least weird - the server can easily ignore EDNS0 options and send a
+ reply, instead of sending error.
+ Currently, udns does nothing in this situation, completely ignoring the
+ error returned by the server, and continue waiting for reply. It probably
+ should grok that this server does not understand EDNS0 and retry w/o the
+ options, but it does not. The end result - esp. if your local DNS
+ server or - worse - broken firewall which inspects DNS packets and drops
+ the ones which - from its point of view - are "broken" - is that you
+ see only TEMPFAIL errors from the library trying to resolve ANY names.
+
+Implementing TCP mode (together with non-EDNS0 fall-back as above) 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.
+
+Choosing of DNS QueryID
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This is more a TODO item really. Currently, udns uses sequential number for
+query IDs. Which simplifies attacks even more (c.f. the previous item about
+single UDP port), making them nearly trivial. The library should use random
+number for query ID. But there's no portable way to get random numbers, even
+on various flavors of Unix. It's possible to use low bits from tv_nsec field
+returned by gettimeofday() (current time, nanoseconds), but I wrote the library
+in a way to avoid making system calls where possible, because many syscalls
+means many context switches and slow processes as a result. Maybe use some
+application-supplied callback to get random values will be a better way,
+defaulting to gettimeofday() method.
+
+Note that a single query - even if (re)sent to different nameservers, several
+times (due to no reply received in time), uses the same qID assigned when it
+was first dispatched. So we have: single UDP socket (fixed port number),
+sequential (= trivially predictable) qIDs, and long lifetime of those qIDs.
+This all makes (local) attacks against the library really trivial.
+
+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... Well ok, to be fair, I don't really remember how
+udns handles this now - need to check this in source... ;)
+
+CNAME recursion
+~~~~~~~~~~~~~~~
+
+Another dark point of udns 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.
+
+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 0.1 version is a way how queries are
+submitted.
+
+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.
diff --git a/libs/udns/TODO b/libs/udns/TODO
new file mode 100644
index 0000000000..ce5bba1272
--- /dev/null
+++ b/libs/udns/TODO
@@ -0,0 +1,74 @@
+$Id: TODO,v 1.10 2005/04/19 21:48:09 mjt Exp $
+
+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 = udns_query_alloc(ctx);
+ udns_query_set(q, options, domain_name, flags, ...);
+ udns_query_submit(ctx, q);
+ or
+ udns_query_resolve(ctx, q);
+
+* 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?
+
+* initialize/open the context automatically, and be more
+ liberal about initialization in general?
+
+* dns_init(do_open) - make the parameter opposite, aka
+ dns_init(skip_open) ?
+
+* for the above. Use separate routine for initializing the context
+ from system files, to not link stuff reading resolv.conf if it's
+ not needed. So that automatic init will not be possible.
+
+* allow TCP queue?
+
+* detect servers which don't understand EDNS0 (returning FORMERR),
+ and fall back to pre-EDNS0 for them.
+
+* for the above to work, we have to be able to find query object by
+ only ID, not ID + qdn.
+
+* And oh, qID should really be random.
+
+* 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?
+
+* windows port? Oh no please!.. At least, I can't do it myself
+ because of the lack of platform.
+ Ok ok, the Windows port is in progress. Christian Prahauser
+ from cosy.sbg.ac.at is helping with that.
diff --git a/libs/udns/debian/changelog b/libs/udns/debian/changelog
new file mode 100644
index 0000000000..c393fea06a
--- /dev/null
+++ b/libs/udns/debian/changelog
@@ -0,0 +1,94 @@
+udns (0.0.9pre) unstable; urgency=low
+
+ * s/EOVERFLOW/ENFILE, partly to make win32 happy
+
+ * several win32 fixes
+
+ * don't use `class' in udns.h, to make C++ happy
+ (thanks Markus Koetter for pointing this out)
+
+ * fixed CNAME handling in dnsget tool. Another Thank You! goes
+ to Markus Koetter.
+
+ * NAPTR (RFC3403) support, thanks to Mikael Magnusson
+ for this.
+
+ * more Win32 fixes from Mikael Magnusson. I have to admit
+ I never tried to compile it on Win32.
+
+ * added NOTES file
+
+ -- Michael Tokarev Wed, 29 Nov 2006 04:16:21 +0300
+
+udns (0.0.8) unstable; urgency=low
+
+ * don't compare sockaddr_in's, but individual parts only
+ (on some OSes, there are additional fields in this structure
+ so memcmp() does not quite work)
+
+ * use dnsc_t instead of unsigned char for DNs everywhere
+
+ * SRV records (rfc2782) parsing, thanks to
+ Thadeu Lima de Souza Cascardo.
+
+ * manpage fixes
+
+ -- Michael Tokarev Mon, 12 Sep 2005 16:06:45 +0400
+
+udns (0.0.7) unstable; urgency=low
+
+ * added dnsget.1 and rblcheck.1 manpages.
+
+ * internal: use generic list implementation in udns_resolver.c
+
+ * don't assume only backward DN pointers in replies, allow forward
+ pointers too. This is quite large change, involves changing
+ parsing API all over the places.
+
+ * internal: typedef dnsc_t and dnscc_t for [const] unsigned char, to
+ make function prototypes shorter to better fit on a single line.
+
+ * in parsing routines, verify (assert) that the query type
+ is the one we can handle.
+
+ * recognize names (and resolve them) as nameservers in dnsget.
+
+ * when new request is submitted, don't send it immediately, but
+ add it into the head of the active queue with deadline=0.
+ This will allow us to tweak some query settings before it will
+ be processed.
+ Note API change: removed `now' argument from all dns_submit_*()
+ routines.
+
+ * use single per-context user timer, not per-query.
+ Note API change: different user timer callback
+
+ * add dnsc_salen field -- length of the socket address used
+
+ * fix dns_set_opt(DNS_OPT_FLAGS) which didn't work before
+
+ * allow to set some options for a context wich is open but with no
+ active queries
+
+ -- Michael Tokarev Thu, 5 May 2005 23:14:29 +0400
+
+udns (0.0.6) unstable; urgency=low
+
+ * 0.0.6 release.
+ ALOT of changes all over. Keep 'em in CVS logs!
+
+ -- Michael Tokarev Fri, 8 Apr 2005 19:51:38 +0400
+
+udns (0.0.5) unstable; urgency=low
+
+ * Initial Release.
+ * Provides 3 packages:
+ libudns0 - shared library
+ libudns-dev - development files and static library
+ udns-utils - dnsget, rblcheck
+
+ -- Michael Tokarev Thu, 7 Apr 2005 00:05:24 +0400
+
+Local variables:
+mode: debian-changelog
+End:
diff --git a/libs/udns/debian/control b/libs/udns/debian/control
new file mode 100644
index 0000000000..f7234f54ac
--- /dev/null
+++ b/libs/udns/debian/control
@@ -0,0 +1,33 @@
+Source: udns
+Priority: optional
+Maintainer: Michael Tokarev
+Build-Depends: debhelper (>= 4.0.0)
+Standards-Version: 3.6.1
+
+Package: libudns0
+Section: lib
+Architecture: any
+Depends: ${shlibs:Depends}
+Description: async-capable DNS stub resolver library
+ libudns0 package provides libudns shared library needed
+ to run programs using it
+
+Package: libudns-dev
+Section: libdev
+Architecture: any
+Depends: libudns0 (= ${Source-Version})
+Description: async-capable DNS stub resolver library, development files
+ This package provides development files needed
+ to build programs using udns library
+
+Package: udns-utils
+Section: net
+Architecture: any
+Depends: ${shlibs:Depends}
+Conflicts: rblcheck
+Description: Several DNS-related utilities built on top of udns library
+ This package includes the following utilities:
+ dnsget - a simple DNS query tool, like `host' or `dig' for usage from
+ a command line, and dnsip, dnsname etc for usage in scripts
+ rblcheck - DNSBL (rbl) checker
+ All the utilities are built using udns library
diff --git a/libs/udns/debian/copyright b/libs/udns/debian/copyright
new file mode 100644
index 0000000000..089491bc69
--- /dev/null
+++ b/libs/udns/debian/copyright
@@ -0,0 +1,24 @@
+This is udns, written and maintained by Michael Tokarev
+
+The original source can always be found at:
+ http://www.corpit.ru/mjt/udns.html
+
+Copyright (C) 2005 Michael Tokarev
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this package; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+
+On Debian systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
diff --git a/libs/udns/debian/rules b/libs/udns/debian/rules
new file mode 100755
index 0000000000..07dc8b874f
--- /dev/null
+++ b/libs/udns/debian/rules
@@ -0,0 +1,91 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+export DH_COMPAT=4
+
+CFLAGS = -Wall -W -Wmissing-prototypes -g
+CDEFS = -DHAVE_POOL
+
+INSTALL = install
+INSTALL_PROGRAM = $(INSTALL) -p
+INSTALL_DATA = $(INSTALL) -p -m0644
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+ CFLAGS += -O0
+else
+ CFLAGS += -O2
+endif
+ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+ INSTALL_PROGRAM += -s
+endif
+ifeq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+# CDEFS += -DNDEBUG
+endif
+
+SOVER = 0
+
+configure: # nothing
+ dh_testdir
+
+build: build-stamp
+build-stamp:
+ dh_testdir
+ $(MAKE) CFLAGS="$(CFLAGS)" CDEFS="$(CDEFS)" SOVER=$(SOVER) \
+ staticlib sharedlib rblcheck.shared dnsget.shared
+ mv -f rblcheck.shared rblcheck
+ mv -f dnsget.shared dnsget
+ touch $@
+
+clean:
+ dh_testdir
+ rm -f build-stamp
+ $(MAKE) distclean
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean
+ dh_installdirs
+ dh_installdocs -A NEWS
+
+# libudns
+ dh_install -plibudns$(SOVER) libudns.so.$(SOVER) usr/lib
+
+# libudns-dev
+ dh_install -plibudns-dev libudns.a libudns.so usr/lib
+ dh_install -plibudns-dev udns.h usr/include
+ dh_installman -plibudns-dev udns.3
+ dh_installdocs -plibudns-dev TODO NOTES
+ dh_installexamples -plibudns-dev ex-rdns.c
+
+# udns-utils
+ dh_installdirs -pudns-utils usr/bin
+ dh_install -pudns-utils dnsget rblcheck usr/bin
+ dh_installman -pudns-utils dnsget.1 rblcheck.1
+
+binary-indep: build install
+
+binary-arch: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs
+ dh_installdocs
+ dh_strip
+ dh_compress
+ dh_fixperms
+ dh_makeshlibs
+ dh_installdeb
+ dh_shlibdeps -L libudns$(SOVER) -l debian/libudns$(SOVER)/usr/lib
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/libs/udns/dnsget.1 b/libs/udns/dnsget.1
new file mode 100644
index 0000000000..711f29769e
--- /dev/null
+++ b/libs/udns/dnsget.1
@@ -0,0 +1,182 @@
+.\" $Id: dnsget.1,v 1.3 2005/04/20 00:55:34 mjt Exp $
+.\" dnsget 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 dnsget 1 "Apr 2005" "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 option : value \]
+.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 \fIoption\fR:\fIvalue\fR
+Set resolver option \fIoption\fR to the value \fIvalue\fR
+(may be specified several times). The same as setting
+.RB $ RES_OPTIONS
+environment variable. The following options are recognized:
+.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).
+.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/libs/udns/dnsget.c b/libs/udns/dnsget.c
new file mode 100644
index 0000000000..1d89a52808
--- /dev/null
+++ b/libs/udns/dnsget.c
@@ -0,0 +1,709 @@
+/* $Id: dnsget.c,v 1.22 2006/11/29 21:28:49 mjt Exp $
+ 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
+
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "udns.h"
+
+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);
+}
+
+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("\\%02x", *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(";EDNS0 OPT record (UDPsize: %d): %d bytes\n",
+ rr->dnsrr_cls, 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", inet_ntop(AF_INET6, dptr, (char*)dn, DNS_MAXDN));
+ 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", inet_ntoa(*((struct in_addr*)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: /* flags(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:
+ /* 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("%d %u %u %u ",
+ 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;
+
+#if 0 /* unused RR types? */
+ case DNS_T_DS:
+ c = dptr;
+ if (c + 2 + 2 >= dend) goto xperr;
+ printf("%u %u %u ", dns_get16(c), c[2], c[3]);
+ printhex(c + 4, dend);
+ break;
+
+ case DNS_T_NSEC:
+ 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)) {
+ char buf[4*4];
+ printf("%s port %d\n",
+ inet_ntop(AF_INET, &((struct sockaddr_in*)sa)->sin_addr,
+ buf, sizeof(buf)),
+ htons(((struct sockaddr_in*)sa)->sin_port));
+ }
+#ifdef AF_INET6
+ else if (sa->sa_family == AF_INET6 && slen >= sizeof(struct sockaddr_in6)) {
+ char buf[6*5+4*4];
+ printf("%s port %d\n",
+ inet_ntop(AF_INET6, &((struct sockaddr_in6*)sa)->sin6_addr,
+ buf, sizeof(buf)),
+ 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_rd(pkt)) printf(" rd");
+ if (dns_ra(pkt)) printf(" ra");
+ if (dns_aa(pkt)) printf(" aa");
+ if (dns_tc(pkt)) printf(" tc");
+ 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;
+ p.dnsp_qcls = 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;
+
+ 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(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: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':
+ if (dns_set_opts(NULL, optarg) != 0)
+ die(0, "invalid option string: `%s'", optarg);
+ 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, AA, 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 option:value - set resovler option (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"
+" (may be specified more than once)\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 (!inet_aton(ns[i], &sin.sin_addr)) {
+ 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", inet_ntoa(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);
+
+ 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 (inet_pton(AF_INET, name, &a.addr) > 0) {
+ dns_a4todn(&a.addr, 0, dn, sizeof(dn));
+ l_qtyp = DNS_T_PTR;
+ abs = 1;
+ }
+ else if (inet_pton(AF_INET6, name, &a.addr6) > 0) {
+ dns_a6todn(&a.addr6, 0, dn, sizeof(dn));
+ l_qtyp = DNS_T_PTR;
+ abs = 1;
+ }
+ 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/libs/udns/ex-rdns.c b/libs/udns/ex-rdns.c
new file mode 100644
index 0000000000..bc705c74d7
--- /dev/null
+++ b/libs/udns/ex-rdns.c
@@ -0,0 +1,111 @@
+/* $Id: ex-rdns.c,v 1.6 2005/05/07 12:21:42 mjt Exp $
+ 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
+#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, t;
+ time_t now;
+ int maxq = 10;
+ struct pollfd pfd;
+ char linebuf[1024];
+ char *eol;
+ int eof;
+
+ if (dns_init(1) < 0) 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 (!inet_aton(linebuf, &pa.a))
+ 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) {
+ t = dns_timeouts(0, -1, now);
+ t = poll(&pfd, 1, c * 1000);
+ now = time(NULL);
+ if (t) dns_ioevent(0, now);
+ }
+ }
+ return 0;
+}
diff --git a/libs/udns/rblcheck.1 b/libs/udns/rblcheck.1
new file mode 100644
index 0000000000..0e427e4395
--- /dev/null
+++ b/libs/udns/rblcheck.1
@@ -0,0 +1,151 @@
+.\" $Id: rblcheck.1,v 1.1 2005/04/24 23:14:23 mjt Exp $
+.\" 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/libs/udns/rblcheck.c b/libs/udns/rblcheck.c
new file mode 100644
index 0000000000..77bc1bfd7f
--- /dev/null
+++ b/libs/udns/rblcheck.c
@@ -0,0 +1,364 @@
+/* $Id: rblcheck.c,v 1.9 2005/04/24 22:49:10 mjt Exp $
+ 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
+
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "udns.h"
+
+static const char *version = "udns-rblcheck 0.1";
+static char *progname;
+
+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) {
+ fprintf(stderr, "%s: out of memory\n", progname);
+ exit(1);
+ }
+ 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';
+ addzone(p);
+ }
+ fclose(f);
+ return 1;
+}
+
+static void dnserror(struct rblookup *ipl, const char *what) {
+ fprintf(stderr, "%s: unable to %s for %s (%s): %s\n",
+ progname, what, inet_ntoa(ipl->key), ipl->zone,
+ dns_strerror(dns_status(0)));
+ ++failures;
+}
+
+static void display_result(struct ipcheck *ipc) {
+ int j;
+ struct rblookup *l, *le;
+ 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) {
+ if (ipc->name) printf("%s[%s]", ipc->name, inet_ntoa(l->key));
+ else printf("%s", inet_ntoa(l->key));
+ }
+ 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 ? " " : "", inet_ntoa(l->addr->dnsa4_addr[j]));
+ 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
+ fprintf(stderr, "%s: unable to lookup %s: %s\n",
+ progname, ipc->name, dns_strerror(dns_status(ctx)));
+}
+
+static int submit(struct ipcheck *ipc) {
+ struct in_addr addr;
+ if (inet_aton(ipc->name, &addr)) {
+ submit_a_queries(ipc, 1, &addr);
+ ipc->name = NULL;
+ }
+ else if (!dns_submit_a4(0, ipc->name, 0, namecb, ipc))
+ fprintf(stderr, "%s: unable to submit name query for %s: %s\n",
+ progname, ipc->name, dns_strerror(dns_status(0)));
+ 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;
+ fprintf(stderr, "%s: unable to read %s\n", progname, optarg);
+ return 1;
+ 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.\n", progname, 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:
+ fprintf(stderr, "%s: use `%s -h' for help\n", progname, progname);
+ return 1;
+ }
+
+ if (!zgiven) {
+ char *s = getenv("RBLCHECK_ZONES");
+ if (s) {
+ char *k;
+ s = strdup(s);
+ k = strtok(s, " \t");
+ while(k) {
+ addzone(k);
+ k = strtok(NULL, " \t");
+ }
+ free(s);
+ }
+ else {
+ 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) {
+ fprintf(stderr, "%s: no service (zone) list specified (-s or -S option)\n",
+ progname);
+ return 1;
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (!argc)
+ return 0;
+
+ if (dns_init(0) < 0) {
+ fprintf(stderr, "%s: unable to initialize DNS library: %s\n",
+ progname, strerror(errno));
+ return 1;
+ }
+ if (nameserver) {
+ dns_add_serv(NULL, NULL);
+ if (dns_add_serv(NULL, nameserver) < 0)
+ fprintf(stderr, "%s: unable to use nameserver %s: %s\n",
+ progname, nameserver, strerror(errno));
+ }
+ if (dns_open(NULL) < 0) {
+ fprintf(stderr, "%s: unable to initialize DNS library: %s\n",
+ progname, strerror(errno));
+ return 1;
+ }
+
+ for (c = 0; c < argc; ++c) {
+ if (c && (verbose > 1 || (verbose == 1 && do_txt))) putchar('\n');
+ 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/libs/udns/udns.3 b/libs/udns/udns.3
new file mode 100644
index 0000000000..06d2a146c4
--- /dev/null
+++ b/libs/udns/udns.3
@@ -0,0 +1,1284 @@
+.\" $Id: udns.3,v 1.26 2006/11/28 22:58:04 mjt Exp $
+.\" udns library 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 udns 3 "Apr 2005" "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 dns.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_MAXSRCH\fR (5 search list entries)"
+Maximum number of domain search list
+.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
+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$DNSCACHEIP\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
+int \fBdns_init\fR(int \fIdo_open\fR)
+struct dns_ctx *\fBdns_new\fR(struct dns_ctx *\fIcopy\fR)
+void \fBdns_free\fR(\fIctx\fR)
+.fi
+.RS
+\fBdns_init\fR() initializes default resolver context, \fBdns_defctx\fR,
+and optionally opens it (if \fIdo_open\fR is true) using \fBdns_open\fR(),
+returning negative value on error.
+\fBdns_new\fR() makes a copy of a given resolver context \fIcopy\fR, or
+default context if \fIcopy\fR is NULL, and returns pointer to it.
+\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 ok to free \fBdns_defctx\fR as well as
+dynamically allocated contexts returned by \fBdns_new\fR().
+.RE
+.PP
+.nf
+int \fBdns_add_serv\fR(\fIctx\fR, const char *\fIserv\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 \fIserv\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.
+.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).
+\fBdns_sock\fR() return the UDP socket if open, or -1 if not.
+\fBdns_close\fR() closes the UDP socket if it was open.
+.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_cbck\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_cbck\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_cbck\fR().
+.PP
+When a timer expires, an application should call \fBdns_tmeouts\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 allocates memory for the
+\fIquery structure\fR and passes it to the library using \fBdns_submit\fR()
+routines, 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\fI
+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 *\fBservices\fR; /* service parameters */
+ 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 dns.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 dns.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.
+
+.SH AUTHOR
+.PP
+The \fBudns\fR library has been written by Michael Tokarev, mjt@corpit.ru.
diff --git a/libs/udns/udns.h b/libs/udns/udns.h
new file mode 100644
index 0000000000..7e89b853c8
--- /dev/null
+++ b/libs/udns/udns.h
@@ -0,0 +1,736 @@
+/* $Id: udns.h,v 1.41 2006/11/29 01:20:30 mjt Exp $
+ 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.0.9pre"
+
+#ifdef WIN32
+# 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_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_MAXSRCH 5 /* max searchlist entries */
+#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_NSEC = 47, /* DNSSEC */
+ 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_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);
+
+/**************************************************************************/
+/**************** 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++ = (n >> 8) & 255; *d++ = n & 255; return d;
+}
+static __inline dnsc_t *dns_put32(dnsc_t *d, unsigned n) {
+ *d++ = (n >> 24) & 255; *d++ = (n >> 16) & 255;
+ *d++ = (n >> 8) & 255; *d++ = 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 = 0x70, /* reserved */
+ 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)
+};
+
+/* 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;
+
+/* initialize default resolver context and open it if do_open is true.
+ * <0 on failure. */
+UDNS_API int
+dns_init(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; 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 array (with ports etc) */
+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_PASSALL = 0x00080000, /* pass all replies to application */
+};
+
+/* 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);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* include guard */
diff --git a/libs/udns/udns_bl.c b/libs/udns/udns_bl.c
new file mode 100644
index 0000000000..dc6f53b164
--- /dev/null
+++ b/libs/udns/udns_bl.c
@@ -0,0 +1,160 @@
+/* $Id: udns_bl.c,v 1.10 2005/09/12 10:55:21 mjt Exp $
+ 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/libs/udns/udns_codes.c b/libs/udns/udns_codes.c
new file mode 100644
index 0000000000..a5ffce7eda
--- /dev/null
+++ b/libs/udns/udns_codes.c
@@ -0,0 +1,171 @@
+/* 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_NSEC,"NSEC"},
+ {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_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_NSEC: return dns_typetab[43].name;
+ case DNS_T_TSIG: return dns_typetab[44].name;
+ case DNS_T_IXFR: return dns_typetab[45].name;
+ case DNS_T_AXFR: return dns_typetab[46].name;
+ case DNS_T_MAILB: return dns_typetab[47].name;
+ case DNS_T_MAILA: return dns_typetab[48].name;
+ case DNS_T_ANY: return dns_typetab[49].name;
+ case DNS_T_ZXFR: return dns_typetab[50].name;
+ case DNS_T_MAX: return dns_typetab[51].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/libs/udns/udns_dn.c b/libs/udns/udns_dn.c
new file mode 100644
index 0000000000..1264a1dc82
--- /dev/null
+++ b/libs/udns/udns_dn.c
@@ -0,0 +1,382 @@
+/* $Id: udns_dn.c,v 1.7 2006/11/28 22:45:20 mjt Exp $
+ 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) {
+ dnsc_t *p;
+ unsigned n;
+ dnscc_t *s = ((dnscc_t *)addr) + 4;
+ while(--s >= (dnscc_t *)addr) {
+ n = *s;
+ 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) {
+ unsigned n;
+ dnscc_t *s = ((dnscc_t *)addr) + 16;
+ if (dn + 64 > dne) return 0;
+ while(--s >= (dnscc_t *)addr) {
+ *dn++ = 1;
+ n = *s & 0x0f;
+ *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/libs/udns/udns_dntosp.c b/libs/udns/udns_dntosp.c
new file mode 100644
index 0000000000..933463e05a
--- /dev/null
+++ b/libs/udns/udns_dntosp.c
@@ -0,0 +1,30 @@
+/* $Id: udns_dntosp.c,v 1.5 2005/04/19 21:48:09 mjt Exp $
+ 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/libs/udns/udns_misc.c b/libs/udns/udns_misc.c
new file mode 100644
index 0000000000..0db7ffd145
--- /dev/null
+++ b/libs/udns/udns_misc.c
@@ -0,0 +1,67 @@
+/* $Id: udns_misc.c,v 1.8 2005/04/05 22:51:32 mjt Exp $
+ 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/libs/udns/udns_parse.c b/libs/udns/udns_parse.c
new file mode 100644
index 0000000000..5440d92414
--- /dev/null
+++ b/libs/udns/udns_parse.c
@@ -0,0 +1,169 @@
+/* $Id: udns_parse.c,v 1.14 2005/09/12 10:55:21 mjt Exp $
+ 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/libs/udns/udns_resolver.c b/libs/udns/udns_resolver.c
new file mode 100644
index 0000000000..f6a647e3cf
--- /dev/null
+++ b/libs/udns/udns_resolver.c
@@ -0,0 +1,1334 @@
+/* $Id: udns_resolver.c,v 1.57 2006/11/29 01:17:43 mjt Exp $
+ 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 WIN32
+# include /* includes */
+# include /* needed for struct in6_addr */
+# include /* for dns server addresses etc */
+# undef HAVE_POLL
+#else
+# include
+# include
+# include
+# include /* for inet_pton() */
+# include
+# include
+# include
+# ifdef HAVE_CONFIG_H
+# include
+# endif
+# ifdef HAVE_POLL
+# include
+# endif
+# define closesocket(sock) close(sock)
+#endif /* !WIN32 */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "udns.h"
+
+#define DNS_QEXTRA 16 /* size of extra buffer space */
+#define DNS_QBUF DNS_HSIZE+DNS_MAXDN+DNS_QEXTRA
+
+#if !defined(HAVE_INET6) && defined(AF_INET6)
+# define HAVE_INET6 1
+#endif
+#ifdef NO_INET6
+# undef HAVE_INET6
+#endif
+
+#ifndef EAFNOSUPPORT
+# define EAFNOSUPPORT EINVAL
+#endif
+
+union sockaddr_ns {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+#if HAVE_INET6
+ struct sockaddr_in6 sin6;
+#endif
+};
+
+struct dns_qlink {
+ struct dns_query *next, *prev;
+};
+
+struct dns_query {
+ struct dns_qlink dnsq_link; /* list entry (should be first) */
+ dnsc_t dnsq_buf[DNS_QBUF]; /* the query buffer */
+ enum dns_class dnsq_cls; /* requested RR class */
+ enum dns_type dnsq_typ; /* requested RR type */
+ unsigned dnsq_len; /* length of the query packet */
+ unsigned dnsq_origdnl; /* original length of the dnsq_dn */
+ 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_try; /* number of tries made so far */
+ unsigned dnsq_srchi; /* current search index */
+ 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
+};
+
+/* working with dns_query lists */
+
+static __inline void qlist_init(struct dns_qlink *list) {
+ list->next = list->prev = (struct dns_query *)list;
+}
+
+static __inline int qlist_empty(const struct dns_qlink *list) {
+ return list->next == (const struct dns_query *)list ? 1 : 0;
+}
+
+static __inline struct dns_query *qlist_first(struct dns_qlink *list) {
+ return list->next == (struct dns_query *)list ? 0 : list->next;
+}
+
+static __inline void qlist_remove(struct dns_query *q) {
+ q->dnsq_link.next->dnsq_link.prev = q->dnsq_link.prev;
+ q->dnsq_link.prev->dnsq_link.next = q->dnsq_link.next;
+}
+
+static __inline struct dns_query *qlist_pop(struct dns_qlink *list) {
+ struct dns_query *q = list->next;
+ if (q == (struct dns_query *)list)
+ return NULL;
+ qlist_remove(q);
+ return q;
+}
+
+/* insert q between prev and next */
+static __inline void
+qlist_insert(struct dns_query *q,
+ struct dns_query *prev, struct dns_query *next) {
+ q->dnsq_link.next = next;
+ q->dnsq_link.prev = prev;
+ prev->dnsq_link.next = next->dnsq_link.prev = q;
+}
+
+static __inline void
+qlist_insert_after(struct dns_query *q, struct dns_query *prev) {
+ qlist_insert(q, prev, prev->dnsq_link.next);
+}
+
+static __inline void
+qlist_insert_before(struct dns_query *q, struct dns_query *next) {
+ qlist_insert(q, next->dnsq_link.prev, next);
+}
+
+static __inline void
+qlist_add_tail(struct dns_query *q, struct dns_qlink *top) {
+ qlist_insert_before(q, (struct dns_query *)top);
+}
+
+static __inline void
+qlist_add_head(struct dns_query *q, struct dns_qlink *top) {
+ qlist_insert_after(q, (struct dns_query *)top);
+}
+
+#define QLIST_FIRST(list, direction) ((list)->direction)
+#define QLIST_ISLAST(list, q) ((q) == (struct dns_query*)(list))
+#define QLIST_NEXT(q, direction) ((q)->dnsq_link.direction)
+
+#define QLIST_FOR_EACH(list, q, direction) \
+ for(q = QLIST_FIRST(list, direction); \
+ !QLIST_ISLAST(list, q); q = QLIST_NEXT(q, direction))
+
+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 */
+ /* search list for unqualified names */
+ dnsc_t dnsc_srch[DNS_MAXSRCH][DNS_MAXDN];
+ unsigned dnsc_nsrch; /* number of srch[] */
+
+ 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 */
+ unsigned short dnsc_nextid; /* next queue ID to use */
+ int dnsc_udpsock; /* UDP socket */
+ struct dns_qlink 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,offset) (*((unsigned*)(((char*)ctx)+offset)))
+
+#define ISSPACE(x) (x == ' ' || x == '\t' || x == '\r' || x == '\n')
+
+static const char space[] = " \t\r\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(qlist_empty(&ctx->dnsc_qactive))
+#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;
+ QLIST_FOR_EACH(&ctx->dnsc_qactive, q, next) {
+ assert(q->dnsq_ctx == ctx);
+ assert(q->dnsq_link.next->dnsq_link.prev == q);
+ assert(q->dnsq_link.prev->dnsq_link.next == q);
+ ++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 */
+ DNS_SEEN_FAIL = 0x0008, /* search: SERVFAIL has been received */
+ DNS_SEEN_WRONG = 0x0010, /* search: something wrong happened */
+};
+
+static int dns_add_serv_internal(struct dns_ctx *ctx, const char *serv) {
+ union sockaddr_ns *sns;
+ 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 HAVE_INET6
+ { struct in_addr addr;
+ struct in6_addr addr6;
+ if (inet_pton(AF_INET, serv, &addr) > 0) {
+ sns->sin.sin_family = AF_INET;
+ sns->sin.sin_addr = addr;
+ return ++ctx->dnsc_nserv;
+ }
+ if (inet_pton(AF_INET6, serv, &addr6) > 0) {
+ sns->sin6.sin6_family = AF_INET6;
+ sns->sin6.sin6_addr = addr6;
+ return ++ctx->dnsc_nserv;
+ }
+ }
+#else
+ { struct in_addr addr;
+ if (inet_aton(serv, &addr) > 0) {
+ sns->sin.sin_family = AF_INET;
+ sns->sin.sin_addr = addr;
+ return ++ctx->dnsc_nserv;
+ }
+ }
+#endif
+ errno = EINVAL;
+ return -1;
+}
+
+int dns_add_serv(struct dns_ctx *ctx, const char *serv) {
+ SETCTXFRESH(ctx);
+ return dns_add_serv_internal(ctx, serv);
+}
+
+static void dns_set_serv_internal(struct dns_ctx *ctx, char *serv) {
+ ctx->dnsc_nserv = 0;
+ for(serv = strtok(serv, space); serv; serv = strtok(NULL, space))
+ dns_add_serv_internal(ctx, serv);
+}
+
+static int
+dns_add_serv_s_internal(struct dns_ctx *ctx, const struct sockaddr *sa) {
+ if (!sa)
+ return (ctx->dnsc_nserv = 0);
+ if (ctx->dnsc_nserv >= DNS_MAXSERV)
+ return errno = ENFILE, -1;
+#if HAVE_INET6
+ 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_add_serv_s(struct dns_ctx *ctx, const struct sockaddr *sa) {
+ SETCTXFRESH(ctx);
+ return dns_add_serv_s_internal(ctx, sa);
+}
+
+static void dns_set_opts_internal(struct dns_ctx *ctx, const char *opts) {
+ unsigned i, v;
+ for(;;) {
+ while(ISSPACE(*opts)) ++opts;
+ if (!*opts) break;
+ for(i = 0; i < sizeof(dns_opts)/sizeof(dns_opts[0]); ++i) {
+ 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') break;
+ do v = v * 10 + (*opts++ - '0');
+ while (*opts >= '0' && *opts <= '9');
+ if (dns_opts[i].min && v < dns_opts[i].min) v = dns_opts[i].min;
+ else if (v > dns_opts[i].max) v = dns_opts[i].max;
+ dns_ctxopt(ctx, dns_opts[i].offset) = v;
+ break;
+ }
+ while(*opts && !ISSPACE(*opts)) ++opts;
+ }
+}
+
+int dns_set_opts(struct dns_ctx *ctx, const char *opts) {
+ SETCTXINACTIVE(ctx);
+ dns_set_opts_internal(ctx, opts);
+ return 0;
+}
+
+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, dns_opts[i].offset);
+ if (val >= 0) {
+ unsigned v = val;
+ if (v < dns_opts[i].min || v > dns_opts[i].max) {
+ errno = EINVAL;
+ return -1;
+ }
+ dns_ctxopt(ctx, dns_opts[i].offset) = 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;
+}
+
+static int dns_add_srch_internal(struct dns_ctx *ctx, const char *srch) {
+ if (!srch)
+ return (ctx->dnsc_nsrch = 0);
+ else if (ctx->dnsc_nsrch >= DNS_MAXSRCH)
+ return errno = ENFILE, -1;
+ else if (dns_sptodn(srch, ctx->dnsc_srch[ctx->dnsc_nsrch], DNS_MAXDN) <= 0)
+ return errno = EINVAL, -1;
+ else
+ return ++ctx->dnsc_nsrch;
+}
+
+int dns_add_srch(struct dns_ctx *ctx, const char *srch) {
+ SETCTXINACTIVE(ctx);
+ return dns_add_srch_internal(ctx, srch);
+}
+
+static void dns_set_srch_internal(struct dns_ctx *ctx, char *srch) {
+ ctx->dnsc_nsrch = 0;
+ for(srch = strtok(srch, space); srch; srch = strtok(NULL, space))
+ dns_add_srch_internal(ctx, srch);
+}
+
+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;
+ if (!ctx->dnsc_utmfn)
+ return;
+ q = QLIST_FIRST(&ctx->dnsc_qactive, next);
+ if (QLIST_ISLAST(&ctx->dnsc_qactive, q))
+ deadline = -1, timeout = -1;
+ else if (!now || q->dnsq_deadline <= now)
+ deadline = 0, timeout = 0;
+ else
+ deadline = q->dnsq_deadline, timeout = deadline - now;
+ if (ctx->dnsc_utmexp == deadline)
+ return;
+ ctx->dnsc_utmfn(ctx, timeout, ctx->dnsc_utmctx);
+ ctx->dnsc_utmexp = deadline;
+}
+
+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;
+}
+
+static void dns_firstid(struct dns_ctx *ctx) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ctx->dnsc_nextid = (tv.tv_usec ^ getpid()) & 0xffff;
+}
+
+#ifdef WIN32
+
+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 && ctx->dnsc_nserv <= DNS_MAXSERV;
+ pAddr = pAddr->Next)
+ for (pDnsAddr = pAddr->FirstDnsServerAddress;
+ pDnsAddr && ctx->dnsc_nserv <= DNS_MAXSERV;
+ pDnsAddr = pDnsAddr->Next)
+ dns_add_serv_s_internal(ctx, pDnsAddr->Address.lpSockaddr);
+ ret = 0;
+freemem:
+ free(pAddrBuf);
+freelib:
+ FreeLibrary(h_iphlpapi);
+ return ret;
+}
+
+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, valBuf, &len);
+ if (res != ERROR_SUCCESS || !len || !valBuf[0]) {
+ len = sizeof(valBuf) - 1;
+ res = RegQueryValueEx(hk, "DhcpNameServer", NULL, &type, 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;
+}
+
+static int dns_init_internal(struct dns_ctx *ctx) {
+ if (dns_initns_iphlpapi(ctx) != 0)
+ dns_initns_registry(ctx);
+ /*XXX WIN32: probably good to get default domain and search list too...
+ * And options. Something is in registry. */
+ /*XXX WIN32: maybe environment variables are also useful? */
+ return 0;
+}
+
+#else /* !WIN32 */
+
+static int dns_init_internal(struct dns_ctx *ctx) {
+ char *v;
+ char buf[2049]; /* this buffer is used to hold /etc/resolv.conf */
+
+ /* 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));
+ else if (strcmp(line, "search") == 0)
+ dns_set_srch_internal(ctx, v);
+ else if (strcmp(line, "nameserver") == 0)
+ dns_add_serv_internal(ctx, strtok(v, space));
+ else if (strcmp(line, "options") == 0)
+ dns_set_opts_internal(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);
+ }
+ if ((v = getenv("RES_OPTIONS")) != NULL)
+ dns_set_opts_internal(ctx, v);
+
+ /* if still no search list, use local domain name */
+ if (!ctx->dnsc_nsrch &&
+ gethostname(buf, sizeof(buf) - 1) == 0 &&
+ (v = strchr(buf, '.')) != NULL &&
+ *++v != '\0')
+ dns_add_srch_internal(ctx, v);
+
+ return 0;
+}
+
+#endif /* dns_init_internal() for !WIN32 */
+
+int dns_init(int do_open) {
+ struct dns_ctx *ctx = &dns_defctx;
+ assert(!CTXINITED(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;
+ qlist_init(&ctx->dnsc_qactive);
+ if (dns_init_internal(ctx) != 0)
+ return -1;
+ dns_firstid(ctx);
+ ctx->dnsc_flags |= DNS_INITED;
+ return do_open ? dns_open(ctx) : 0;
+}
+
+struct dns_ctx *dns_new(const struct dns_ctx *ctx) {
+ struct dns_ctx *n;
+ SETCTXINITED(ctx);
+ dns_assert_ctx(ctx);
+ n = malloc(sizeof(*n));
+ if (!n)
+ return NULL;
+ *n = *ctx;
+ n->dnsc_udpsock = -1;
+ qlist_init(&n->dnsc_qactive);
+ n->dnsc_nactive = 0;
+ n->dnsc_pbuf = NULL;
+ n->dnsc_qstatus = 0;
+ n->dnsc_utmfn = NULL;
+ n->dnsc_utmctx = NULL;
+ dns_firstid(n);
+ return n;
+}
+
+void dns_free(struct dns_ctx *ctx) {
+ struct dns_query *q;
+ SETCTXINITED(ctx);
+ dns_assert_ctx(ctx);
+ dns_drop_utm(ctx);
+ if (ctx->dnsc_udpsock >= 0)
+ closesocket(ctx->dnsc_udpsock);
+ if (ctx->dnsc_pbuf)
+ free(ctx->dnsc_pbuf);
+ while((q = qlist_pop(&ctx->dnsc_qactive)))
+ free(q);
+ if (ctx != &dns_defctx)
+ free(ctx);
+ else
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+int dns_open(struct dns_ctx *ctx) {
+ int sock;
+ unsigned i;
+ int port;
+ union sockaddr_ns *sns;
+#if HAVE_INET6
+ unsigned have_inet6 = 0;
+#endif
+
+ SETCTXINITED(ctx);
+ assert(!CTXOPEN(ctx));
+
+ port = htons(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 */
+#if HAVE_INET6
+ if (sns->sa.sa_family == AF_INET6) {
+ if (!sns->sin6.sin6_port) sns->sin6.sin6_port = port;
+ ++have_inet6;
+ }
+ else
+#endif
+ {
+ assert(sns->sa.sa_family == AF_INET);
+ if (!sns->sin.sin_port) sns->sin.sin_port = port;
+ }
+ }
+
+#if !HAVE_INET6
+ ctx->dnsc_salen = sizeof(struct sockaddr_in);
+#else
+ 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;
+ ((struct in_addr*)&sin6.sin6_addr)[3] = sns->sin.sin_addr;
+ 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
+#endif /* HAVE_INET6 */
+ sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ if (sock < 0) {
+ ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
+ return -1;
+ }
+#ifdef WIN32
+ { unsigned long on = 1;
+ if (ioctlsocket(sock, FIONBIO, &on) == SOCKET_ERROR) {
+ closesocket(sock);
+ ctx->dnsc_qstatus = DNS_E_TEMPFAIL;
+ return -1;
+ }
+ }
+#else /* !WIN32 */
+ 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 /* WIN32 */
+ /* allocate the packet buffer */
+ if (!(ctx->dnsc_pbuf = malloc(ctx->dnsc_udpbuf))) {
+ closesocket(sock);
+ ctx->dnsc_qstatus = DNS_E_NOMEM;
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ctx->dnsc_udpsock = sock;
+ return sock;
+}
+
+void dns_close(struct dns_ctx *ctx) {
+ SETCTXINITED(ctx);
+ if (ctx->dnsc_udpsock < 0) return;
+ closesocket(ctx->dnsc_udpsock);
+ ctx->dnsc_udpsock = -1;
+ free(ctx->dnsc_pbuf);
+ ctx->dnsc_pbuf = NULL;
+}
+
+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 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;
+ /* 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)
+
+/* Try next search, filling in qDN in query.
+ * Return new qDN len or 0 if no more to search.
+ * Caller should fill up the rest of the query.
+ */
+static unsigned dns_next_srch(const struct dns_ctx *ctx, struct dns_query *q) {
+ unsigned ol = q->dnsq_origdnl - 1; /* origdnl is at least 1 */
+ dnsc_t *p = dns_payload(q->dnsq_buf) + ol;
+ dnscc_t *dn;
+ int n;
+ while (q->dnsq_srchi < ctx->dnsc_nsrch) {
+ dn = ctx->dnsc_srch[q->dnsq_srchi++];
+ if (!*dn) { /* root dn */
+ if (!(q->dnsq_flags & DNS_ASIS_DONE))
+ break;
+ }
+ else if ((n = dns_dntodn(dn, p, DNS_MAXDN - ol)) > 0)
+ return n + ol;
+ }
+ if (q->dnsq_flags & DNS_ASIS_DONE)
+ return 0;
+ q->dnsq_flags |= DNS_ASIS_DONE;
+ *p = '\0';
+ return ol + 1;
+}
+
+/* find the next server which isn't skipped starting from current.
+ * return 0 if ok, >0 if ok but we started next cycle, or <0 if
+ * number of tries exceeded or no more servers.
+ */
+static int dns_find_serv(const struct dns_ctx *ctx, struct dns_query *q) {
+ int cycle;
+ if (q->dnsq_try < ctx->dnsc_ntries) for(cycle = 0;;) {
+ if (q->dnsq_servi < ctx->dnsc_nserv) {
+ if (!(q->dnsq_servskip & (1 << q->dnsq_servi)))
+ return cycle;
+ ++q->dnsq_servi;
+ }
+ else if (cycle || ++q->dnsq_try >= ctx->dnsc_ntries)
+ break;
+ else {
+ cycle = 1;
+ q->dnsq_servi = 0;
+ }
+ }
+ return -1;
+}
+
+/* send the query out and add it to the active list. */
+static void dns_send(struct dns_ctx *ctx, struct dns_query *q, time_t now) {
+ int n;
+ struct dns_query *p;
+
+ /* 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 there's no more servers, fail the query */
+ n = dns_find_serv(ctx, q);
+ if (n < 0) {
+ dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0);
+ return;
+ }
+
+ /* send the query */
+ n = 10;
+ while (sendto(ctx->dnsc_udpsock, q->dnsq_buf, q->dnsq_len, 0,
+ &ctx->dnsc_serv[q->dnsq_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 (--n) continue;
+ /* if we can't send the query, fail it. */
+ dns_end_query(ctx, q, DNS_E_TEMPFAIL, 0);
+ return;
+ }
+ DNS_DBGQ(ctx, q, 1,
+ &ctx->dnsc_serv[q->dnsq_servi].sa, sizeof(union sockaddr_ns),
+ q->dnsq_buf, q->dnsq_len);
+ q->dnsq_servwait |= 1 << q->dnsq_servi; /* expect reply from this ns */
+
+ /* advance to the next server, and choose a timeout.
+ * we will try next server in 1 secound, but start next
+ * cycle waiting for proper timeout. */
+ ++q->dnsq_servi;
+ n = dns_find_serv(ctx, q) ? ctx->dnsc_timeout << (q->dnsq_try - 1) : 1;
+
+ q->dnsq_deadline = now = now + n;
+
+ /* insert the query to the tail of the list */
+ QLIST_FOR_EACH(&ctx->dnsc_qactive, p, prev)
+ if (p->dnsq_deadline <= now)
+ break;
+ qlist_insert_after(q, p);
+
+}
+
+static void dns_dummy_cb(struct dns_ctx *ctx, void *result, void *data) {
+ if (result) free(result);
+ data = ctx = 0; /* used */
+}
+
+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) {
+ dnsc_t *p;
+ unsigned dnl;
+ 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;
+
+ flags = (flags | ctx->dnsc_flags) & ~DNS_INTERNAL;
+ if (!ctx->dnsc_nsrch) q->dnsq_flags |= DNS_NOSRCH;
+ if (!(flags & DNS_NORD)) q->dnsq_buf[DNS_H_F1] |= DNS_HF1_RD;
+ if (flags & DNS_AAONLY) q->dnsq_buf[DNS_H_F1] |= DNS_HF1_AA;
+ q->dnsq_buf[DNS_H_QDCNT2] = 1;
+ dns_put16(q->dnsq_buf + DNS_H_QID, ctx->dnsc_nextid++);
+
+ q->dnsq_origdnl = dns_dnlen(dn);
+ assert(q->dnsq_origdnl > 0 && q->dnsq_origdnl <= DNS_MAXDN);
+ memcpy(dns_payload(q->dnsq_buf), dn, q->dnsq_origdnl);
+ p = dns_payload(q->dnsq_buf) + q->dnsq_origdnl;
+ if (flags & DNS_NOSRCH || dns_dnlabels(dn) > ctx->dnsc_ndots)
+ flags |= DNS_ASIS_DONE;
+ else if ((dnl = dns_next_srch(ctx, q)) > 0)
+ p = dns_payload(q->dnsq_buf) + dnl;
+ else
+ p[-1] = '\0';
+ q->dnsq_flags = flags;
+ q->dnsq_typ = qtyp;
+ p = dns_put16(p, qtyp);
+ q->dnsq_cls = qcls;
+ p = dns_put16(p, qcls);
+ if (ctx->dnsc_udpbuf > DNS_MAXPACKET) {
+ p++; /* empty (root) DN */
+ p = dns_put16(p, DNS_T_OPT);
+ p = dns_put16(p, ctx->dnsc_udpbuf);
+ p += 2; /* EDNS0 RCODE & VERSION */
+ p += 2; /* rest of the TTL field */
+ p += 2; /* RDLEN */
+ q->dnsq_buf[DNS_H_ARCNT2] = 1;
+ }
+ assert(p <= q->dnsq_buf + DNS_QBUF);
+ q->dnsq_len = p - q->dnsq_buf;
+
+ qlist_add_head(q, &ctx->dnsc_qactive);
+ ++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.
+ * Current loop/goto looks just terrible... */
+void dns_ioevent(struct dns_ctx *ctx, time_t now) {
+ int r;
+ unsigned servi, l;
+ 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:
+
+ for(;;) { /* receive the reply */
+ dnsc_t dn[DNS_MAXDN];
+
+ slen = sizeof(sns);
+ r = recvfrom(ctx->dnsc_udpsock, pbuf, ctx->dnsc_udpbuf, 0, &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 WIN32
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+#else
+ if (errno == EAGAIN)
+#endif
+ {
+ dns_request_utm(ctx, now);
+ return;
+ }
+ continue;
+ }
+ /* ignore replies from wrong server */
+#if HAVE_INET6
+ if (sns.sa.sa_family == AF_INET6 && slen >= sizeof(sns.sin6)) {
+ for (servi = 0; servi < ctx->dnsc_nserv; ++servi)
+ if (ctx->dnsc_serv[servi].sin6.sin6_port == sns.sin6.sin6_port &&
+ memcmp(&ctx->dnsc_serv[servi].sin6.sin6_addr,
+ &sns.sin6.sin6_addr, sizeof(sns.sin6.sin6_addr)) == 0)
+ break;
+ }
+ else
+#endif
+ if (sns.sa.sa_family == AF_INET && slen >= sizeof(sns.sin)) {
+ for (servi = 0; servi < ctx->dnsc_nserv; ++servi)
+ if (ctx->dnsc_serv[servi].sin.sin_addr.s_addr == sns.sin.sin_addr.s_addr &&
+ ctx->dnsc_serv[servi].sin.sin_port == sns.sin.sin_port)
+ break;
+ }
+ else {
+ DNS_DBG(ctx, -1, &sns.sa, slen, pbuf, r);
+ continue;
+ }
+ if (servi >= ctx->dnsc_nserv) {
+ DNS_DBG(ctx, -2, &sns.sa, slen, pbuf, r);
+ continue;
+ }
+
+ pend = pbuf + r;
+ pcur = dns_payload(pbuf);
+ if (pcur >= pend || dns_numqd(pbuf) != 1 || dns_opcode(pbuf) != 0 ||
+ dns_getdn(pbuf, &pcur, pend, dn, sizeof(dn)) < 0 ||
+ pcur + 4 > pend) {
+ /*XXX ignore non-query replies and replies with numqd!=1? */
+ DNS_DBG(ctx, -3, &sns.sa, slen, pbuf, r);
+ continue;
+ }
+
+ /* truncation bit (TC). Ooh, we don't handle TCP (yet?),
+ * but we do handle larger UDP sizes.
+ * Note that e.g. djbdns will only send header if resp.
+ * does not fit, not whatever is fit in 512 bytes. */
+ if (dns_tc(pbuf)) {
+ DNS_DBG(ctx, -4, &sns.sa, slen, pbuf, r);
+ continue; /* just ignore response for now.. any hope? */
+ }
+
+ /* find the request for this reply in active queue
+ * Note we pick any request, even queued for another
+ * server - in case first server replies a bit later
+ * than we expected. */
+ for (q = QLIST_FIRST(&ctx->dnsc_qactive, next);; q = QLIST_NEXT(q, next)) {
+ if (QLIST_ISLAST(&ctx->dnsc_qactive, q)) {
+ /* no more requests: old reply? */
+ DNS_DBG(ctx, -5, &sns.sa, slen, pbuf, r);
+ goto again;
+ }
+ /* ignore replies that has not been sent to this server.
+ * Note dnsq_servi is the *next* server to try. */
+ if (!q->dnsq_try && q->dnsq_servi <= servi)
+ continue;
+ /*XXX ignore replies from servers we're ignoring? o/
+ if (q->dnsq_servskip & (1 << servi))
+ continue; */
+ /* check qID */
+ if (q->dnsq_buf[DNS_H_QID1] != pbuf[DNS_H_QID1] ||
+ q->dnsq_buf[DNS_H_QID2] != pbuf[DNS_H_QID2])
+ continue;
+ /* check qDN, qCLS and qTYP */
+ if (!(l = dns_dnequal(dn, dns_payload(q->dnsq_buf))) ||
+ memcmp(pcur, dns_payload(q->dnsq_buf) + l, 4) != 0)
+ continue;
+ /* ok, this is expected reply with matching query. */
+ break;
+ }
+
+ break;
+
+ }
+
+ DNS_DBGQ(ctx, q, 0, &sns.sa, slen, pbuf, r);
+
+ /* we got a reply for our query */
+ q->dnsq_servwait &= ~(1 << servi); /* don't expect reply from this serv */
+
+ /* process the RCODE */
+ switch(dns_rcode(pbuf)) {
+
+ case DNS_R_NOERROR:
+ qlist_remove(q);
+ if (!dns_numan(pbuf)) { /* no data of requested type */
+ q->dnsq_flags |= DNS_SEEN_NODATA;
+ r = DNS_E_NODATA;
+ break;
+ }
+ /* the only case where we may succeed */
+ if (q->dnsq_parse) {
+ r = q->dnsq_parse(dns_payload(q->dnsq_buf), pbuf, pcur, pend, &result);
+ if (r < 0)
+ result = NULL;
+ }
+ else if ((result = malloc(r)) != NULL)
+ memcpy(result, pbuf, r);
+ else
+ r = DNS_E_NOMEM;
+ /* (maybe) successeful answer (modulo nomem and parsing probs) */
+ /* note we pass DNS_E_NODATA here */
+ dns_end_query(ctx, q, r, result);
+ goto again;
+
+ case DNS_R_NXDOMAIN:
+ qlist_remove(q);
+ r = DNS_E_NXDOMAIN;
+ break;
+
+ case DNS_R_SERVFAIL:
+ q->dnsq_flags |= DNS_SEEN_FAIL;
+ case DNS_R_NOTIMPL:
+ case DNS_R_REFUSED:
+ /* for these rcodes, advance this request
+ * to the next server and reschedule */
+ default: /* unknown rcode? hmmm... */
+ /* try next server */
+ q->dnsq_servskip |= 1 << servi; /* don't retry this server */
+ if (!q->dnsq_servwait) {
+ qlist_remove(q);
+ dns_send(ctx, q, now);
+ }
+ else {
+ /* else this is the only place where q will be left unconnected
+ * if we will move qlist_remove() before the switch{}. */
+ }
+ goto again;
+
+ }
+
+ /* here we have either NODATA or NXDOMAIN */
+ if (!(q->dnsq_flags & DNS_NOSRCH)) {
+ /* try next element from search list */
+ unsigned sl;
+
+ l = dns_dnlen(dns_payload(q->dnsq_buf)) + DNS_HSIZE; /* past qDN */
+ /* save qcls, qtyp and EDNS0 stuff (of len sl) in pbuf */
+ sl = q->dnsq_len - l;
+ memcpy(pbuf, q->dnsq_buf + l, sl);
+ /* try next search list */
+ l = dns_next_srch(ctx, q);
+ if (l) { /* something else to try, of len l */
+ l += DNS_HSIZE;
+ memcpy(q->dnsq_buf + l, pbuf, sl);
+ q->dnsq_len = l + sl;
+ q->dnsq_try = 0; q->dnsq_servi = 0;
+ q->dnsq_servwait = q->dnsq_servskip = 0;
+ dns_send(ctx, q, now);
+ goto again;
+ }
+ /* else we have nothing more to search, end the query. */
+ if (q->dnsq_flags & DNS_SEEN_FAIL)
+ /* at least one server/query failed, fail the query */
+ r = DNS_E_TEMPFAIL;
+ else if (q->dnsq_flags & DNS_SEEN_NODATA)
+ /* for one domain we have seen NODATA, return it */
+ r = DNS_E_NODATA;
+ else /* else all should be NXDOMAINs */
+ r = DNS_E_NXDOMAIN;
+ }
+
+ dns_end_query(ctx, q, r, 0);
+ goto again;
+}
+
+/* handle all timeouts */
+int dns_timeouts(struct dns_ctx *ctx, int maxwait, time_t now) {
+ struct dns_query *q;
+ int w;
+ SETCTX(ctx);
+ dns_assert_ctx(ctx);
+ if (!now) now = time(NULL);
+ while((q = qlist_first(&ctx->dnsc_qactive)) && q->dnsq_deadline <= now) {
+ qlist_remove(q);
+ dns_send(ctx, q, now);
+ }
+ dns_request_utm(ctx, now);
+ if (!q)
+ return maxwait;
+ w = q->dnsq_deadline - now;
+ return maxwait < 0 || maxwait > w ? w : 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;
+#ifdef HAVE_POLL
+ struct pollfd pfd;
+#else
+ fd_set rfd;
+ struct timeval tv;
+#endif
+ 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;
+
+#ifdef HAVE_POLL
+ pfd.fd = ctx->dnsc_udpsock;
+ pfd.events = POLLIN;
+#else
+ FD_ZERO(&rfd);
+#endif
+
+ now = time(NULL);
+ while(!d.dnsrd_done && (n = dns_timeouts(ctx, -1, now)) >= 0) {
+#ifdef HAVE_POLL
+ n = poll(&pfd, 1, n * 1000);
+#else
+ tv.tv_sec = n;
+ tv.tv_usec = 0;
+ FD_SET(ctx->dnsc_udpsock, &rfd);
+ 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(q);
+ --ctx->dnsc_nactive;
+ dns_request_utm(ctx, 0);
+ return 0;
+}
+
diff --git a/libs/udns/udns_rr_a.c b/libs/udns/udns_rr_a.c
new file mode 100644
index 0000000000..2266252614
--- /dev/null
+++ b/libs/udns/udns_rr_a.c
@@ -0,0 +1,124 @@
+/* $Id: udns_rr_a.c,v 1.14 2005/04/20 06:44:34 mjt Exp $
+ 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
+#ifdef WIN32
+# include /* includes */
+# include /* needed for struct in6_addr */
+#else
+# 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) {
+ assert(sizeof(struct in_addr) == 4);
+ 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/libs/udns/udns_rr_mx.c b/libs/udns/udns_rr_mx.c
new file mode 100644
index 0000000000..cf8348354f
--- /dev/null
+++ b/libs/udns/udns_rr_mx.c
@@ -0,0 +1,91 @@
+/* $Id: udns_rr_mx.c,v 1.13 2005/04/20 06:44:34 mjt Exp $
+ 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/libs/udns/udns_rr_naptr.c b/libs/udns/udns_rr_naptr.c
new file mode 100644
index 0000000000..ec4f514601
--- /dev/null
+++ b/libs/udns/udns_rr_naptr.c
@@ -0,0 +1,128 @@
+/* $Id: udns_rr_naptr.c,v 1.1 2006/11/28 22:58:04 mjt Exp $
+ 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/libs/udns/udns_rr_ptr.c b/libs/udns/udns_rr_ptr.c
new file mode 100644
index 0000000000..410af656a5
--- /dev/null
+++ b/libs/udns/udns_rr_ptr.c
@@ -0,0 +1,109 @@
+/* $Id: udns_rr_ptr.c,v 1.15 2005/09/12 11:21:06 mjt Exp $
+ 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/libs/udns/udns_rr_srv.c b/libs/udns/udns_rr_srv.c
new file mode 100644
index 0000000000..56bbbf7f78
--- /dev/null
+++ b/libs/udns/udns_rr_srv.c
@@ -0,0 +1,154 @@
+/* $Id: udns_rr_srv.c,v 1.2 2005/09/12 12:26:22 mjt Exp $
+ 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 unsigned add_sname(dnsc_t *dn, const char *sn) {
+ unsigned l;
+ 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
+ * protocol (service may be NULL in which case protocol isn't used).
+ * Return negative value on error (malformed query),
+ * or addition query flag(s) to use.
+ */
+static int
+build_srv_dn(dnsc_t *dn, const char *name, const char *srv, const char *proto)
+{
+ unsigned p = 0, l;
+ int isabs;
+ if (srv) {
+ l = add_sname(dn + p, srv);
+ if (!l)
+ return -1;
+ p += l;
+ 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)
+ 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/libs/udns/udns_rr_txt.c b/libs/udns/udns_rr_txt.c
new file mode 100644
index 0000000000..ecc71f06ed
--- /dev/null
+++ b/libs/udns/udns_rr_txt.c
@@ -0,0 +1,98 @@
+/* $Id: udns_rr_txt.c,v 1.15 2006/11/28 22:45:20 mjt Exp $
+ 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/include/switch_types.h b/src/include/switch_types.h
index 06e803b1f4..1b08cbe5a8 100644
--- a/src/include/switch_types.h
+++ b/src/include/switch_types.h
@@ -86,6 +86,8 @@ SWITCH_BEGIN_EXTERN_C
#define SWITCH_LOCAL_MEDIA_PORT_VARIABLE "_local_media_port_"
#define SWITCH_REMOTE_MEDIA_IP_VARIABLE "_remote_media_ip_"
#define SWITCH_REMOTE_MEDIA_PORT_VARIABLE "_remote_media_port_"
+#define SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE "hangup_after_bridge"
+
#define SWITCH_SPEECH_KEY "_speech_"
#define SWITCH_UUID_BRIDGE "_uuid_bridge_"
#define SWITCH_BITS_PER_BYTE 8
diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h
index 2ff270eb8f..8886eaa552 100644
--- a/src/include/switch_utils.h
+++ b/src/include/switch_utils.h
@@ -39,6 +39,7 @@
#define SWITCH_UTILS_H
#include
+#include
SWITCH_BEGIN_EXTERN_C
@@ -231,8 +232,15 @@ SWITCH_DECLARE(int) switch_socket_waitfor(switch_pollfd_t *poll, int ms);
*/
SWITCH_DECLARE(char *) switch_cut_path(char *in);
+#define switch_clean_re(re) if (re) {\
+ pcre_free(re);\
+ re = NULL;\
+ }
+
SWITCH_DECLARE(char *) switch_string_replace(const char *string, const char *search, const char *replace);
SWITCH_DECLARE(switch_status_t) switch_string_match(const char *string, size_t string_len, const char *search, size_t search_len);
+SWITCH_DECLARE(int) switch_perform_regex(char *field, char *expression, pcre **new_re, int *ovector, uint32_t olen);
+SWITCH_DECLARE(void) switch_perform_substitution(pcre *re, int match_count, char *data, char *field_data, char *substituted, uint32_t len, int *ovector);
#define SWITCH_READ_ACCEPTABLE(status) status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK
SWITCH_DECLARE(size_t) switch_url_encode(char *url, char *buf, size_t len);
diff --git a/src/mod/applications/mod_bridgecall/mod_bridgecall.c b/src/mod/applications/mod_bridgecall/mod_bridgecall.c
index 52281bd489..87039ff408 100644
--- a/src/mod/applications/mod_bridgecall/mod_bridgecall.c
+++ b/src/mod/applications/mod_bridgecall/mod_bridgecall.c
@@ -45,6 +45,10 @@ static void audio_bridge_function(switch_core_session_t *session, char *data)
uint8_t no_media_bridge = 0;
switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;
+ if (switch_strlen_zero(data)) {
+ return;
+ }
+
caller_channel = switch_core_session_get_channel(session);
assert(caller_channel != NULL);
diff --git a/src/mod/codecs/mod_gsm/mod_gsm.c b/src/mod/codecs/mod_gsm/mod_gsm.c
index e64d4e387c..f0e4063a71 100644
--- a/src/mod/codecs/mod_gsm/mod_gsm.c
+++ b/src/mod/codecs/mod_gsm/mod_gsm.c
@@ -135,7 +135,7 @@ static switch_status_t switch_gsm_decode(switch_codec_t *codec, switch_codec_t *
static const switch_codec_implementation_t gsm_8k_implementation = {
/*.codec_type */ SWITCH_CODEC_TYPE_AUDIO,
/*.ianacode */ 3,
- /*.iananame */ "gsm",
+ /*.iananame */ "GSM",
/*.fmtp */ NULL,
/*.samples_per_second */ 8000,
/*.bits_per_second */ 13200,
diff --git a/src/mod/dialplans/mod_dialplan_xml/mod_dialplan_xml.c b/src/mod/dialplans/mod_dialplan_xml/mod_dialplan_xml.c
index 9d6488f4bb..5897b66861 100644
--- a/src/mod/dialplans/mod_dialplan_xml/mod_dialplan_xml.c
+++ b/src/mod/dialplans/mod_dialplan_xml/mod_dialplan_xml.c
@@ -37,96 +37,8 @@
static const char modname[] = "mod_dialplan_xml";
-#define cleanre(re) if (re) {\
- pcre_free(re);\
- re = NULL;\
- }
-static int perform_regex(switch_channel_t *channel, char *field, char *expression, pcre **new_re, int *ovector, uint32_t olen)
-{
- const char *error = NULL;
- int erroffset = 0;
- pcre *re = NULL;
- int match_count = 0;
-
- if (!(field && expression)) {
- return 0;
- }
-
- re = pcre_compile(expression, /* the pattern */
- 0, /* default options */
- &error, /* for error message */
- &erroffset, /* for error offset */
- NULL); /* use default character tables */
- if (error) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "COMPILE ERROR: %d [%s]\n", erroffset, error);
- cleanre(re);
- return 0;
- }
-
- match_count = pcre_exec(re, /* result of pcre_compile() */
- NULL, /* we didn't study the pattern */
- field, /* the subject string */
- (int) strlen(field), /* the length of the subject string */
- 0, /* start at offset 0 in the subject */
- 0, /* default options */
- ovector, /* vector of integers for substring information */
- olen); /* number of elements (NOT size in bytes) */
-
- if (match_count <= 0) {
- cleanre(re);
- match_count = 0;
- }
-
- *new_re = re;
-
- return match_count;
-}
-
-
-static void perform_substitution(pcre *re, int match_count, char *data, char *field_data, char *substituted, uint32_t len, int *ovector)
-{
- char index[10] = "";
- char replace[1024] = "";
- uint32_t x, y = 0, z = 0, num = 0;
-
- for (x = 0; x < (len-1) && x < strlen(data);) {
- if (data[x] == '$') {
- x++;
-
- if (!(data[x] > 47 && data[x] < 58)) {
- substituted[y++] = data[x-1];
- continue;
- }
-
- while (data[x] > 47 && data[x] < 58) {
- index[z++] = data[x];
- x++;
- }
- index[z++] = '\0';
- z = 0;
- num = atoi(index);
-
- if (pcre_copy_substring(field_data,
- ovector,
- match_count,
- num,
- replace,
- sizeof(replace)) > 0) {
- unsigned int r;
- for (r = 0; r < strlen(replace); r++) {
- substituted[y++] = replace[r];
- }
- }
- } else {
- substituted[y++] = data[x];
- x++;
- }
- }
- substituted[y++] = '\0';
-}
-
typedef enum {
BREAK_ON_TRUE,
BREAK_ON_FALSE,
@@ -197,7 +109,7 @@ static int parse_exten(switch_core_session_t *session, switch_xml_t xexten, swit
field_data = "";
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "test conditions %s(%s) =~ /%s/\n", field, field_data, expression);
- if (!(proceed = perform_regex(channel, field_data, expression, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
+ if (!(proceed = switch_perform_regex(field_data, expression, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Regex mismatch\n");
for (xaction = switch_xml_child(xcond, "anti-action"); xaction; xaction = xaction->next) {
@@ -232,7 +144,7 @@ static int parse_exten(switch_core_session_t *session, switch_xml_t xexten, swit
char *app_data = NULL;
if (field && strchr(expression, '(')) {
- perform_substitution(re, proceed, data, field_data, substituted, sizeof(substituted), ovector);
+ switch_perform_substitution(re, proceed, data, field_data, substituted, sizeof(substituted), ovector);
app_data = substituted;
} else {
app_data = data;
@@ -249,7 +161,7 @@ static int parse_exten(switch_core_session_t *session, switch_xml_t xexten, swit
switch_caller_extension_add_application(session, *extension, application, app_data);
}
- cleanre(re);
+ switch_clean_re(re);
if (do_break_i == BREAK_ON_TRUE || do_break_i == BREAK_ALWAYS) {
break;
diff --git a/src/switch_ivr.c b/src/switch_ivr.c
index 646cd96f88..d8ffc7d591 100644
--- a/src/switch_ivr.c
+++ b/src/switch_ivr.c
@@ -137,7 +137,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session)
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_channel_t *channel;
switch_frame_t *frame;
- int stream_id;
+ int stream_id = 0;
switch_event_t *event;
channel = switch_core_session_get_channel(session);
@@ -152,19 +152,18 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_park(switch_core_session_t *session)
switch_channel_set_flag(channel, CF_CONTROLLED);
while (switch_channel_ready(channel)) {
- for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
- if ((status = switch_core_session_read_frame(session, &frame, -1, stream_id)) == SWITCH_STATUS_SUCCESS) {
- if (!SWITCH_READ_ACCEPTABLE(status)) {
- break;
- }
-
- if (switch_core_session_dequeue_private_event(session, &event) == SWITCH_STATUS_SUCCESS) {
- switch_ivr_parse_event(session, event);
- switch_event_destroy(&event);
- }
-
- }
- }
+
+ if ((status = switch_core_session_read_frame(session, &frame, -1, stream_id)) == SWITCH_STATUS_SUCCESS) {
+ if (!SWITCH_READ_ACCEPTABLE(status)) {
+ break;
+ }
+
+ if (switch_core_session_dequeue_private_event(session, &event) == SWITCH_STATUS_SUCCESS) {
+ switch_ivr_parse_event(session, event);
+ switch_event_destroy(&event);
+ }
+ }
+
}
switch_channel_clear_flag(channel, CF_CONTROLLED);
@@ -966,7 +965,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess
switch_codec_t codec;
switch_memory_pool_t *pool = switch_core_session_get_pool(session);
char *codec_name;
- int stream_id;
+ int stream_id = 0;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_file_handle_t lfh;
switch_codec_t *read_codec = switch_core_session_get_read_codec(session);
@@ -975,11 +974,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess
uint8_t asis = 0;
char *ext;
- if (fh->samples > 0) {
- sample_start = fh->samples;
- fh->samples = 0;
- }
-
if (file) {
if ((ext = strrchr(file, '.'))) {
ext++;
@@ -1000,6 +994,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess
memset(fh, 0, sizeof(lfh));
}
+
+ if (fh->samples > 0) {
+ sample_start = fh->samples;
+ fh->samples = 0;
+ }
+
+
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
@@ -1259,21 +1260,23 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess
if (!asis) {switch_swap_linear(write_frame.data, (int) write_frame.datalen / 2);}
#endif
#endif
- for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
- status = switch_core_session_write_frame(session, &write_frame, -1, stream_id);
+ stream_id = 0;
- if (status == SWITCH_STATUS_MORE_DATA) {
- status = SWITCH_STATUS_SUCCESS;
- continue;
- } else if (status != SWITCH_STATUS_SUCCESS) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n");
- done = 1;
- break;
- }
- if (done) {
- break;
- }
- }
+ status = switch_core_session_write_frame(session, &write_frame, -1, stream_id);
+
+ if (status == SWITCH_STATUS_MORE_DATA) {
+ status = SWITCH_STATUS_SUCCESS;
+ continue;
+ } else if (status != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n");
+ done = 1;
+ break;
+ }
+
+ if (done) {
+ break;
+ }
+
if (timer_name) {
if (switch_core_timer_next(&timer) < 0) {
@@ -1487,7 +1490,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session
switch_size_t ilen = 0;
switch_frame_t write_frame = {0};
int x;
- int stream_id;
+ int stream_id = 0;
int done = 0;
int lead_in_out = 10;
switch_status_t status = SWITCH_STATUS_SUCCESS;
@@ -1522,13 +1525,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session
write_frame.codec = codec;
for( x = 0; !done && x < lead_in_out; x++) {
- for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
- if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n");
- done = 1;
- break;
- }
- }
+ if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n");
+ done = 1;
+ break;
+ }
}
ilen = len;
@@ -1606,13 +1607,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session
if (status != SWITCH_STATUS_SUCCESS) {
for( x = 0; !done && x < lead_in_out; x++) {
- for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
- if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n");
- done = 1;
- break;
- }
- }
+ if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n");
+ done = 1;
+ break;
+ }
}
if (status == SWITCH_STATUS_BREAK) {
status = SWITCH_STATUS_SUCCESS;
@@ -1627,17 +1626,15 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session
write_frame.datalen = (uint32_t)ilen;
write_frame.samples = (uint32_t)(ilen / 2);
- for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
- if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n");
- done = 1;
- break;
- }
+ if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Bad Write\n");
+ done = 1;
+ break;
+ }
- if (done) {
- break;
- }
- }
+ if (done) {
+ break;
+ }
if (timer) {
if ((x = switch_core_timer_next(timer)) < 0) {
@@ -1686,7 +1683,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses
switch_codec_t codec;
switch_memory_pool_t *pool = switch_core_session_get_pool(session);
char *codec_name;
- int stream_id;
+ int stream_id = 0;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_speech_handle_t sh;
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
@@ -3130,8 +3127,6 @@ static const switch_state_handler_table_t signal_bridge_state_handlers = {
/*.on_hibernate*/ signal_bridge_on_hibernate
};
-
-
SWITCH_DECLARE(switch_status_t) switch_ivr_signal_bridge(switch_core_session_t *session, switch_core_session_t *peer_session)
{
switch_channel_t *caller_channel, *peer_channel;
@@ -3319,9 +3314,15 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_multi_threaded_bridge(switch_core_ses
switch_channel_get_name(peer_channel)
);
switch_channel_hangup(peer_channel, SWITCH_CAUSE_NO_ANSWER);
- }
+ }
done:
+
+ if (switch_channel_get_state(caller_channel) < CS_HANGUP &&
+ switch_true(switch_channel_get_variable(caller_channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE))) {
+ switch_channel_hangup(caller_channel, SWITCH_CAUSE_NORMAL_CLEARING);
+ }
+
return status;
}
diff --git a/src/switch_utils.c b/src/switch_utils.c
index 623942898c..baeed869eb 100644
--- a/src/switch_utils.c
+++ b/src/switch_utils.c
@@ -35,6 +35,92 @@
#include
+
+SWITCH_DECLARE(int) switch_perform_regex(char *field, char *expression, pcre **new_re, int *ovector, uint32_t olen)
+{
+ const char *error = NULL;
+ int erroffset = 0;
+ pcre *re = NULL;
+ int match_count = 0;
+
+ if (!(field && expression)) {
+ return 0;
+ }
+
+ re = pcre_compile(expression, /* the pattern */
+ 0, /* default options */
+ &error, /* for error message */
+ &erroffset, /* for error offset */
+ NULL); /* use default character tables */
+ if (error) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "COMPILE ERROR: %d [%s]\n", erroffset, error);
+ switch_clean_re(re);
+ return 0;
+ }
+
+ match_count = pcre_exec(re, /* result of pcre_compile() */
+ NULL, /* we didn't study the pattern */
+ field, /* the subject string */
+ (int) strlen(field), /* the length of the subject string */
+ 0, /* start at offset 0 in the subject */
+ 0, /* default options */
+ ovector, /* vector of integers for substring information */
+ olen); /* number of elements (NOT size in bytes) */
+
+ if (match_count <= 0) {
+ switch_clean_re(re);
+ match_count = 0;
+ }
+
+ *new_re = re;
+
+ return match_count;
+}
+
+
+SWITCH_DECLARE(void) switch_perform_substitution(pcre *re, int match_count, char *data, char *field_data, char *substituted, uint32_t len, int *ovector)
+{
+ char index[10] = "";
+ char replace[1024] = "";
+ uint32_t x, y = 0, z = 0, num = 0;
+
+ for (x = 0; x < (len-1) && x < strlen(data);) {
+ if (data[x] == '$') {
+ x++;
+
+ if (!(data[x] > 47 && data[x] < 58)) {
+ substituted[y++] = data[x-1];
+ continue;
+ }
+
+ while (data[x] > 47 && data[x] < 58) {
+ index[z++] = data[x];
+ x++;
+ }
+ index[z++] = '\0';
+ z = 0;
+ num = atoi(index);
+
+ if (pcre_copy_substring(field_data,
+ ovector,
+ match_count,
+ num,
+ replace,
+ sizeof(replace)) > 0) {
+ unsigned int r;
+ for (r = 0; r < strlen(replace); r++) {
+ substituted[y++] = replace[r];
+ }
+ }
+ } else {
+ substituted[y++] = data[x];
+ x++;
+ }
+ }
+ substituted[y++] = '\0';
+}
+
+
SWITCH_DECLARE(char *) switch_priority_name(switch_priority_t priority)
{
switch(priority) { /*lol*/