[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#767375: marked as done (unblock: pdns-recursor/3.6.2-1)



Your message dated Sat, 1 Nov 2014 00:07:45 +0000
with message-id <20141101000745.GA9785@lupin.home.powdarrmonkey.net>
and subject line Re: Bug#767375: unblock: pdns-recursor/3.6.2-1
has caused the Debian Bug report #767375,
regarding unblock: pdns-recursor/3.6.2-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
767375: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=767375
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Dear Release Team,

Please unblock package pdns-recursor; while the new version is a new
upstream release, it's a bug-fix only release, which fixes at least one
bug in Debian, and a few other ones.
It also incorporates a patch that I carried in the previous version,
thereby reducing the delta between Debian and upstream back to 0.

I'll follow up to this bug once it's built everywhere and such.
I'm attaching the debdiff (which is huge because of the mentioned
patch) and a reduced diff which should be the same apart from the patch
move.

unblock pdns-recursor/3.6.2-1

Thank you,
Christian


-- System Information:
Debian Release: jessie/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (1, 'experimental')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 3.16-3-amd64 (SMP w/8 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
diff -Nru pdns-recursor-3.6.1/build-scripts/redhat/pdns-recursor-test.spec pdns-recursor-3.6.2/build-scripts/redhat/pdns-recursor-test.spec
--- pdns-recursor-3.6.1/build-scripts/redhat/pdns-recursor-test.spec	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/build-scripts/redhat/pdns-recursor-test.spec	2014-10-30 13:25:52.000000000 +0100
@@ -9,7 +9,7 @@
 Epoch:			0
 Group:			System Environment/Daemons
 License:		GPL
-Source:			http://downloads.powerdns.com/releases/%{name}-3.6.1.tar.bz2
+Source:			http://downloads.powerdns.com/releases/%{name}-3.6.2.tar.bz2
 
 BuildRequires:		boost-devel >= 1.39.0
 BuildRequires:		lua-devel >= 5.2
@@ -20,7 +20,7 @@
 PowerDNS recursor
 
 %prep
-%setup -q -n %{name}-3.6.1
+%setup -q -n %{name}-3.6.2
 
 %build
 %configure
diff -Nru pdns-recursor-3.6.1/config.h pdns-recursor-3.6.2/config.h
--- pdns-recursor-3.6.1/config.h	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/config.h	2014-10-30 13:25:52.000000000 +0100
@@ -1,3 +1,4 @@
 #define RECURSOR
-#define VERSION "3.6.1"
+#define VERSION "3.6.2"
 #define DIST_HOST "jenkins@autotest.powerdns.com"
+#define HAVE_BOOST 1
diff -Nru pdns-recursor-3.6.1/debian/changelog pdns-recursor-3.6.2/debian/changelog
--- pdns-recursor-3.6.1/debian/changelog	2014-10-21 21:31:43.000000000 +0200
+++ pdns-recursor-3.6.2/debian/changelog	2014-10-30 17:34:48.000000000 +0100
@@ -1,3 +1,10 @@
+pdns-recursor (3.6.2-1) unstable; urgency=high
+
+  * Imported Upstream version 3.6.2, a bugfix release (Closes: #767368)
+  * Remove API key patch, which has been incorporated upstream.
+
+ -- Christian Hofstaedtler <zeha@debian.org>  Thu, 30 Oct 2014 17:22:19 +0100
+
 pdns-recursor (3.6.1-3) unstable; urgency=medium
 
   * Apply API key patch from upstream
diff -Nru pdns-recursor-3.6.1/debian/patches/apikey-3-6-2.patch pdns-recursor-3.6.2/debian/patches/apikey-3-6-2.patch
--- pdns-recursor-3.6.1/debian/patches/apikey-3-6-2.patch	2014-10-21 21:30:50.000000000 +0200
+++ pdns-recursor-3.6.2/debian/patches/apikey-3-6-2.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,3008 +0,0 @@
-Index: pdns-recursor/config.h
-===================================================================
---- pdns-recursor.orig/config.h	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/config.h	2014-10-21 21:30:12.521562549 +0200
-@@ -1,3 +1,4 @@
- #define RECURSOR
--#define VERSION "3.6.1"
--#define DIST_HOST "jenkins@autotest.powerdns.com"
-+#define VERSION "3.6.1-apikey"
-+#define DIST_HOST "ch@build.deduktiva.com"
-+#define HAVE_BOOST 1
-Index: pdns-recursor/ext/yahttp/.gitignore
-===================================================================
---- pdns-recursor.orig/ext/yahttp/.gitignore	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ext/yahttp/.gitignore	2014-10-21 21:29:06.001312969 +0200
-@@ -1,2 +1,4 @@
- yahttp/libyahttp.a
- *.o
-+Makefile
-+Makefile.in
-Index: pdns-recursor/ext/yahttp/LICENSE
-===================================================================
---- pdns-recursor.orig/ext/yahttp/LICENSE	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ext/yahttp/LICENSE	2014-10-21 21:29:06.001312969 +0200
-@@ -1,502 +1,21 @@
--GNU LESSER GENERAL PUBLIC LICENSE
--                       Version 2.1, February 1999
-+The MIT License (MIT)
- 
-- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-- Everyone is permitted to copy and distribute verbatim copies
-- of this license document, but changing it is not allowed.
-+Copyright (c) 2014 Aki Tuomi
- 
--[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.
--
--    {{description}}
--    Copyright (C) {{year}}  {{fullname}}
--
--    This library is free software; you can redistribute it and/or
--    modify it under the terms of the GNU Lesser General Public
--    License as published by the Free Software Foundation; either
--    version 2.1 of the License, or (at your option) any later version.
--
--    This library is distributed in the hope that it will be useful,
--    but WITHOUT ANY WARRANTY; without even the implied warranty of
--    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
--    Lesser General Public License for more details.
--
--    You should have received a copy of the GNU Lesser General Public
--    License along with this library; if not, write to the Free Software
--    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
--
--Also add information on how to contact you by electronic and paper mail.
--
--You should also get your employer (if you work as a programmer) or your
--school, if any, to sign a "copyright disclaimer" for the library, if
--necessary.  Here is a sample; alter the names:
--
--  Yoyodyne, Inc., hereby disclaims all copyright interest in the
--  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
--
--  {signature of Ty Coon}, 1 April 1990
--  Ty Coon, President of Vice
--
--That's all there is to it!
-+Permission is hereby granted, free of charge, to any person obtaining a copy
-+of this software and associated documentation files (the "Software"), to deal
-+in the Software without restriction, including without limitation the rights
-+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-+copies of the Software, and to permit persons to whom the Software is
-+furnished to do so, subject to the following conditions:
-+
-+The above copyright notice and this permission notice shall be included in
-+all copies or substantial portions of the Software.
-+
-+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-+THE SOFTWARE.
-Index: pdns-recursor/ext/yahttp/Makefile
-===================================================================
---- pdns-recursor.orig/ext/yahttp/Makefile	2014-10-21 21:29:06.005312990 +0200
-+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
-@@ -1,22 +0,0 @@
--.SILENT:
--
--all:
--	$(MAKE) -C yahttp all
--
--install:
--
--uninstall:
--
--distclean: clean
--
--clean:
--	$(MAKE) -C yahttp clean
--
--check:
--
--distdir:
--	mkdir -p $(distdir)
--	cp LICENSE Makefile $(distdir)
--	mkdir $(distdir)/yahttp
--	cp yahttp/Makefile yahttp/*.cpp yahttp/*.hpp $(distdir)/yahttp
--
-Index: pdns-recursor/ext/yahttp/Makefile.am
-===================================================================
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ pdns-recursor/ext/yahttp/Makefile.am	2014-10-21 21:29:06.001312969 +0200
-@@ -0,0 +1,3 @@
-+SUBDIRS=yahttp
-+
-+EXTRA_DIST=LICENSE README.md
-Index: pdns-recursor/ext/yahttp/README.md
-===================================================================
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ pdns-recursor/ext/yahttp/README.md	2014-10-21 21:29:06.001312969 +0200
-@@ -0,0 +1,62 @@
-+Yet Another HTTP Library
-+========================
-+
-+YaHTTP aims to be a pure http request/response parser that has no IO ties. It is intended to be used on small-footprint applications and other utilities that want to use HTTP over something else than network IO.
-+
-+[![Build Status](https://travis-ci.org/cmouse/yahttp.svg?branch=master)](https://travis-ci.org/cmouse/yahttp)
-+[![Coverity Scan Build Status](https://scan.coverity.com/projects/2161/badge.svg)](https://scan.coverity.com/projects/2161)
-+
-+WARNINGS
-+--------
-+If you are upgrading from version before May 02, 2014 - *PLEASE BE SURE TO CHECK THAT EVERYTHING WORKS AS EXPECTED*. There has been complete overhaul of the library and many things have changed. 
-+
-+Integration guide
-+-----------------
-+
-+Here are some instructions on how to integrate YaHTTP into your project. 
-+
-+With automake and libtool
-+-------------------------
-+
-+If you don't need router or any of the C++11 features, you can just create empty yahttp-config.h, or symlink it to your project's config.h for the yahttp.hpp to include. Then just put this stuff into it's own folder and create Makefile.am with following contents (you can change the compilation flags):
-+
-+```
-+noinst_LTLIBRARIES=libyahttp.la
-+libyahttp_la_CXXFLAGS=$(RELRO_CFLAGS) $(PIE_CFLAGS) -D__STRICT_ANSI__
-+libyahttp_la_SOURCES=cookie.hpp exception.hpp reqresp.cpp reqresp.hpp router.cpp router.hpp url.hpp utility.hpp yahttp.hpp
-+```
-+
-+You can define RELRO and PIE to match your project. 
-+
-+To compile your project use -Lpath/to/yahttp -lyahttp
-+
-+If you need router, additionally check for boost or C++11 and replace yahttp-config.h to config.h in yahttp.hpp or add relevant options to your compiler CXXFLAGS. See below for the flags.
-+
-+Without automake
-+----------------
-+
-+Create simple Makefile with contents for C++11:
-+
-+```
-+OBJECTS=reqresp.o router.o
-+CXX=gcc
-+CXXFLAGS=-W -Wall -DHAVE_CXX11 -std=c++11 
-+```
-+
-+Or create simple Makefile with contents for boost:
-+
-+```
-+OBJECTS=reqresp.o router.o
-+CXX=gcc
-+CXXFLAGS=-W -Wall -DHAVE_BOOST 
-+```
-+
-+Or if you don't need either one, just:
-+
-+```
-+OBJECTS=reqresp.o 
-+CXX=gcc
-+CXXFLAGS=-W -Wall
-+```
-+
-+YaHTTP include files can be placed where the rest of your includes are. Then just add your own code there and it should work just fine. 
-Index: pdns-recursor/ext/yahttp/yahttp/cookie.hpp
-===================================================================
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ pdns-recursor/ext/yahttp/yahttp/cookie.hpp	2014-10-21 21:29:06.001312969 +0200
-@@ -0,0 +1,135 @@
-+namespace YaHTTP {
-+  /*! Implements a single cookie */
-+  class Cookie {
-+  public:
-+     Cookie() {
-+       secure = false;
-+       httponly = false;
-+       name = value = "";
-+     }; //!< Set the cookie to empty value
-+
-+     Cookie(const Cookie &rhs) {
-+       domain = rhs.domain;
-+       path = rhs.path;
-+       secure = rhs.secure;
-+       httponly = rhs.httponly;
-+       name = rhs.name;
-+       value = rhs.value;
-+     }; //<! Copy cookie values
-+
-+     DateTime expires; /*!< Expiration date */
-+     std::string domain; /*!< Domain where cookie is valid */
-+     std::string path; /*!< Path where the cookie is valid */
-+     bool httponly; /*!< Whether the cookie is for server use only */
-+     bool secure; /*!< Whether the cookie is for HTTPS only */
-+ 
-+     std::string name; /*!< Cookie name */
-+     std::string value; /*!< Cookie value */
-+
-+     std::string str() const {
-+       std::ostringstream oss;
-+       oss << YaHTTP::Utility::encodeURL(name) << "=" << YaHTTP::Utility::encodeURL(value);
-+       if (expires.isSet) 
-+         oss << "; expires=" << expires.cookie_str();
-+       if (domain.size()>0)
-+         oss << "; domain=" << domain;
-+       if (path.size()>0)
-+         oss << "; path=" << path;
-+       if (secure)
-+         oss << "; secure";
-+       if (httponly)
-+         oss << "; httpOnly";
-+       return oss.str();
-+     }; //!< Stringify the cookie
-+  };
-+
-+  /*! Implements a Cookie jar for storing multiple cookies */
-+  class CookieJar {
-+    public:
-+    std::map<std::string, Cookie> cookies;  //<! cookie container
-+  
-+    CookieJar() {}; //<! constructs empty cookie jar
-+    CookieJar(const CookieJar & rhs) {
-+      this->cookies = rhs.cookies;
-+    } //<! copy cookies from another cookie jar
-+  
-+    void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value) {
-+      size_t pos;
-+      pos = keyvalue.find("=");
-+      if (pos == std::string::npos) throw "Not a Key-Value pair (cookie)";
-+      key = std::string(keyvalue.begin(), keyvalue.begin()+pos);
-+      value = std::string(keyvalue.begin()+pos+1, keyvalue.end());
-+    } //<! key value pair parser
-+  
-+    void parseCookieHeader(const std::string &cookiestr) {
-+      std::list<Cookie> cookies;
-+      int cstate = 0; //cookiestate
-+      size_t pos,npos;
-+      pos = 0;
-+      cstate = 0;
-+      while(pos < cookiestr.size()) {
-+        if (cookiestr.compare(pos, 7, "expires") ==0 ||
-+            cookiestr.compare(pos, 6, "domain")  ==0 ||
-+            cookiestr.compare(pos, 4, "path")    ==0) {
-+          cstate = 1;
-+          // get the date
-+          std::string key, value, s;
-+          npos = cookiestr.find("; ", pos);
-+          if (npos == std::string::npos) {
-+            // last value
-+            s = std::string(cookiestr.begin() + pos + 1, cookiestr.end());
-+            pos = cookiestr.size();
-+          } else {
-+            s = std::string(cookiestr.begin() + pos + 1, cookiestr.begin() + npos - 1);
-+            pos = npos+2;
-+          }
-+          keyValuePair(s, key, value);
-+          if (s == "expires") {
-+            DateTime dt;
-+            dt.parseCookie(value);
-+            for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
-+              i->expires = dt;
-+          } else if (s == "domain") {
-+            for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
-+              i->domain = value;
-+          } else if (s == "path") {
-+            for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
-+              i->path = value;
-+          }
-+        } else if (cookiestr.compare(pos, 8, "httpOnly")==0) {
-+          cstate = 1;
-+          for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
-+            i->httponly = true;
-+        } else if (cookiestr.compare(pos, 6, "secure")  ==0) {
-+          cstate = 1;
-+          for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
-+            i->secure = true;
-+        } else if (cstate == 0) { // expect cookie
-+          Cookie c;
-+          std::string s;
-+          npos = cookiestr.find("; ", pos);
-+          if (npos == std::string::npos) {
-+            // last value
-+            s = std::string(cookiestr.begin() + pos, cookiestr.end());
-+            pos = cookiestr.size();
-+          } else {
-+            s = std::string(cookiestr.begin() + pos, cookiestr.begin() + npos);
-+            pos = npos+2;
-+          }
-+          keyValuePair(s, c.name, c.value);
-+          c.name = YaHTTP::Utility::decodeURL(c.name);
-+          c.value = YaHTTP::Utility::decodeURL(c.value);
-+          cookies.push_back(c);
-+        } else if (cstate == 1) {
-+          // ignore crap
-+          break;
-+        }
-+      }
-+  
-+      // store cookies
-+      for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++) {
-+        this->cookies[i->name] = *i;
-+      }
-+    }; //<! Parse multiple cookies from header 
-+  };
-+};
-Index: pdns-recursor/ext/yahttp/yahttp/exception.hpp
-===================================================================
---- pdns-recursor.orig/ext/yahttp/yahttp/exception.hpp	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ext/yahttp/yahttp/exception.hpp	2014-10-21 21:29:06.005312990 +0200
-@@ -4,17 +4,24 @@
- #include <exception>
- 
- namespace YaHTTP {
--  class ParseError: public std::exception {
-+  /*! Generic error class */
-+  class Error: public std::exception {
-   public:
--    ParseError() {};
--    ParseError(const std::string& reason): reason(reason) {};
--    virtual ~ParseError() throw() {}; 
-+    Error() {};
-+    Error(const std::string& reason): reason(reason) {};
-+    virtual ~Error() throw() {};
- 
-     virtual const char* what() const throw()
-     {
-       return reason.c_str();
-     }
--    const std::string reason;
-+    const std::string reason; //<! Cause of the error
-+  };
-+  /*! Parse error class */
-+  class ParseError: public YaHTTP::Error {
-+  public:
-+    ParseError() {};
-+    ParseError(const std::string& reason): Error(reason) {};
-   };
- };
- 
-Index: pdns-recursor/ext/yahttp/yahttp/Makefile
-===================================================================
---- pdns-recursor.orig/ext/yahttp/yahttp/Makefile	2014-10-21 21:29:06.005312990 +0200
-+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
-@@ -1,14 +0,0 @@
--
--all: libyahttp.a
--
--
--clean:
--	-rm -f *.o *.a
--
--
--reqresp.o: *.cpp *.hpp
--
--
--libyahttp.a: reqresp.o
--	echo AR $@
--	$(AR) rcs $@ $<
-Index: pdns-recursor/ext/yahttp/yahttp/Makefile.am
-===================================================================
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ pdns-recursor/ext/yahttp/yahttp/Makefile.am	2014-10-21 21:29:06.005312990 +0200
-@@ -0,0 +1,3 @@
-+noinst_LTLIBRARIES=libyahttp.la
-+
-+libyahttp_la_SOURCES=cookie.hpp exception.hpp reqresp.cpp reqresp.hpp router.cpp router.hpp url.hpp utility.hpp yahttp-config.h yahttp.hpp
-Index: pdns-recursor/ext/yahttp/yahttp/reqresp.cpp
-===================================================================
---- pdns-recursor.orig/ext/yahttp/yahttp/reqresp.cpp	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ext/yahttp/yahttp/reqresp.cpp	2014-10-21 21:29:06.005312990 +0200
-@@ -1,306 +1,229 @@
--#include "reqresp.hpp"
-+#include "yahttp.hpp"
- 
- namespace YaHTTP {
--
--  void Request::build(const std::string &method, const std::string &url, const std::string &params) {
--    this->method = method;
--    std::transform(this->method.begin(), this->method.end(), this->method.begin(), ::toupper);
--    this->url.parse(url);
--    this->headers["host"] = this->url.host;
--    this->headers["connection"] = "close";
--    this->headers["user-agent"] = "yahttp 0.1";
--    this->headers["accept"] = "*/*";
--    if (params.empty() == false) {
--      this->headers["content-type"] = "application/x-www-form-urlencoded; charset=utf-8";
--      this->headers["content-length"] = params.size();
--      this->body = params;
--    } else {
--      this->body = "";
--    }
--  };
--
--  Request::Request() {};
--  Request::Request(const Response &resp) {
--    method = resp.method;
--    url = resp.url;
--    cookies = resp.cookies;
--  };
--  Request::Request(const Request &req) {
--    method = req.method;
--    url = req.url;
--    parameters = req.parameters;
--    headers = req.headers;
--    cookies = req.cookies;
--    body = req.body;
--  };
--  Request::~Request() {};
--
--  Response::Response() : status(0) {};
--  Response::Response(const Request &req) {
--    headers["connection"] = "close";
--    method = req.method;
--    url = req.url;
--    cookies = req.cookies;
--    status = 200;
--  };
--  Response::Response(const Response &resp) {
--    method = resp.method;
--    url = resp.url;
--    parameters = resp.parameters;
--    headers = resp.headers;
--    cookies = resp.cookies;
--    body = resp.body;
--    status = resp.status;
--    statusText = resp.statusText;
--  };
--  Response::~Response() {};
--
--  void Response::load(std::istream &is) {
--    AsyncResponseLoader arl(this);
--    while(is.good()) {
--      char buf[1024];
--      is.read(buf, 1024);
--      if (is.gcount()) { // did we actually read anything 
--        is.clear();
--        if (arl.feed(std::string(buf, is.gcount())) == true) return; // completed
--      }
--    }
--    // FIXME: parse cookies
--  };
--
--  void Response::write(std::ostream &os) const { 
--    os << "HTTP/1.1 " << status << " ";
--    if (statusText.empty()) 
--      os << Utility::status2text(status);
--    else
--      os << statusText;
--    os << "\r\n";
--
--    // write headers
--    strstr_map_t::const_iterator iter = headers.begin();
--    while(iter != headers.end()) {
--      os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
--      iter++;
--    }
--    os << "\r\n";
--    os << body;
--  };
-- 
--  void Request::load(std::istream &is) {
--    AsyncRequestLoader arl(this);
--    while(is.good()) {
--      char buf[1024];
--      is.read(buf, 1024);
--      if (is.gcount()) { // did we actually read anything
--        is.clear();
--        if (arl.feed(std::string(buf, is.gcount())) == true) return; // completed
--      }
--    }
--  };
--
--  void Request::write(std::ostream &os) const {
--    os << method << " " << url.path << " HTTP/1.1" << "\r\n";
--    strstr_map_t::const_iterator iter = headers.begin();
--    while(iter != headers.end()) {
--      os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
--      iter++;
--    }
--    os << "\r\n";
--    if (body.size()>0) {
--      os << body;
--    }
--  };
--
--  std::ostream& operator<<(std::ostream& os, const Response &resp) {
--    resp.write(os);
--    return os;
--  };
--
--  std::istream& operator>>(std::istream& is, Response &resp) {
--    resp.load(is);
--    return is;
--  };
--
--  std::ostream& operator<<(std::ostream& os, const Request &req) {
--    req.write(os);
--    return os;
--  };
--
--  std::istream& operator>>(std::istream& is, Request &req) {
--    req.load(is);
--    return is;
--  };
--
--  bool AsyncRequestLoader::feed(const std::string &somedata) {
--    size_t pos;
--
-+  template <class T>
-+  int AsyncLoader<T>::feed(const std::string& somedata) {
-     buffer.append(somedata);
-     while(state < 2) {
--      // need to find newline in buffer
--      if ((pos = buffer.find("\r\n")) == std::string::npos) return false;
--      std::string line(buffer.begin(), buffer.begin()+pos); // exclude CRLF
--      buffer.erase(buffer.begin(), buffer.begin()+pos+2); // remove line from buffer including CRLF
-+      int cr=0;
-+      // need to find CRLF in buffer
-+      if ((pos = buffer.find_first_of("\n")) == std::string::npos) return false;
-+      if (buffer[pos-1]=='\r')
-+        cr=1;
-+      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
-+      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
-+
-       if (state == 0) { // startup line
--        std::string ver;
--        std::string tmpurl;
--        std::istringstream iss(line);
--        iss >> request->method >> tmpurl >> ver;
--        if (ver.find("HTTP/1.") != 0)
--          throw ParseError("Not a HTTP 1.x request");
--        // uppercase the request method
--        std::transform(request->method.begin(), request->method.end(), request->method.begin(), ::toupper);
--        request->url.parse(tmpurl);
--        request->parameters = Utility::parseUrlParameters(request->url.parameters);
--        state = 1;
-+        if (target->kind == YAHTTP_TYPE_REQUEST) {
-+          std::string ver;
-+          std::string tmpurl;
-+          std::istringstream iss(line);
-+          iss >> target->method >> tmpurl >> ver;
-+          if (ver.find("HTTP/1.") != 0)
-+            throw ParseError("Not a HTTP 1.x request");
-+          // uppercase the target method
-+          std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
-+          target->url.parse(tmpurl);
-+          target->getvars = Utility::parseUrlParameters(target->url.parameters);
-+          state = 1;
-+        } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
-+          std::string ver;
-+          std::istringstream iss(line);
-+          iss >> ver >> target->status >> target->statusText;
-+          if (ver.find("HTTP/1.") != 0)
-+            throw ParseError("Not a HTTP 1.x response");
-+          state = 1;
-+        }
-       } else if (state == 1) {
-         std::string key,value;
-         size_t pos;
-         if (line.empty()) {
--          chunked = (request->headers.find("transfer-encoding") != request->headers.end() && request->headers["transfer-encoding"] == "chunked");
--          // host header is optional
--          if (request->headers.find("host") != request->headers.end())
--            request->url.host = request->headers["host"];
--               
-+          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
-           state = 2;
-           break;
-         }
-         // split headers
-         if ((pos = line.find_first_of(": ")) == std::string::npos)
--          throw ParseError("Malformed line");
-+          throw ParseError("Malformed header line");
-         key = line.substr(0, pos);
-         value = line.substr(pos+2);
-+        Utility::trimRight(value);
-         std::transform(key.begin(), key.end(), key.begin(), ::tolower);
--        request->headers[key] = value;
-+        // is it already defined
-+
-+        if ((key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) ||
-+            (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST)) {
-+          target->jar.parseCookieHeader(value);
-+        } else {
-+          if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
-+            // maybe it contains port? 
-+            if ((pos = value.find(":")) == std::string::npos) {
-+              target->url.host = value;
-+            } else {
-+              target->url.host = value.substr(0, pos);
-+              target->url.port = ::atoi(value.substr(pos).c_str());
-+            }
-+          }
-+          if (target->headers.find(key) != target->headers.end()) {
-+            target->headers[key] = target->headers[key] + ";" + value;
-+          } else {
-+            target->headers[key] = value;
-+          }
-+        }
-       }
-     }
- 
--    // skip body for GET
--    if (request->method == "GET")
--      return true;
--       
--    // do we have content-length? 
-+    minbody = 0;
-+    // check for expected body size
-+    if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
-+    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size; 
-+    else maxbody = 0;
-+   
-     if (!chunked) {
--      if (request->headers.find("content-length") != request->headers.end()) {
--        std::istringstream maxbodyS(request->headers["content-length"]);
--        maxbodyS >> maxbody;
-+      if (target->headers.find("content-length") != target->headers.end()) {
-+        std::istringstream maxbodyS(target->headers["content-length"]);
-+        maxbodyS >> minbody;
-+        maxbody = minbody;
-       }
--      if (maxbody < 1) return true; // guess there isn't anything left.
--      if (maxbody > YAHTTP_MAX_REQUEST_SIZE) 
--        throw ParseError("Request size exceeded");
-+      if (minbody < 1) return true; // guess there isn't anything left.
-+      if (target->kind == YAHTTP_TYPE_REQUEST && static_cast<ssize_t>(minbody) > target->max_request_size) throw ParseError("Max request body size exceeded");
-+      else if (target->kind == YAHTTP_TYPE_RESPONSE && static_cast<ssize_t>(minbody) > target->max_response_size) throw ParseError("Max response body size exceeded");
-     }
- 
--    if (buffer.size() == 0) return false;
-+    if (maxbody == 0) hasBody = false;
-+    else hasBody = true;
- 
--    while(buffer.size() > 0 && bodybuf.str().size() < static_cast<size_t>(maxbody)) {
--      char buf[1024] = {0};
-+    if (buffer.size() == 0) return ready();
- 
-+    while(buffer.size() > 0) {
-       if (chunked) {
-         if (chunk_size == 0) {
-+          char buf[100];
-           // read chunk length
-           if ((pos = buffer.find('\n')) == std::string::npos) return false;
--          if (pos > 1023)
-+          if (pos > 99)
-             throw ParseError("Impossible chunk_size");
-           buffer.copy(buf, pos);
-           buf[pos]=0; // just in case...
-           buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
-           sscanf(buf, "%x", &chunk_size);
--          if (!chunk_size) break; // last chunk
-+          if (!chunk_size) { state = 3; break; } // last chunk
-         } else {
-+          int crlf=1;
-           if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
--          if (buffer.at(chunk_size) != '\n') return false; // there should be newline.
--          buffer.copy(buf, chunk_size);
--          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+1);
--          bodybuf << buf;
-+          if (buffer.at(chunk_size) == '\r') {
-+            if (buffer.size() < static_cast<size_t>(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
-+            crlf=2;
-+          } else if (buffer.at(chunk_size) != '\n') return false; 
-+          std::string tmp = buffer.substr(0, chunk_size);
-+          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
-+          bodybuf << tmp;
-           chunk_size = 0;
--          if (buffer.size() == 0) return false; // just in case
-+          if (buffer.size() == 0) break; // just in case
-         }
-       } else {
--        bodybuf << buffer;
-+        if (bodybuf.str().length() + buffer.length() > maxbody) 
-+          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
-+        else 
-+          bodybuf << buffer;
-         buffer = "";
-       }
-     }
- 
-     if (chunk_size!=0) return false; // need more data
- 
--    if (!chunked && bodybuf.str().size() < maxbody) {
--      return false; // need more data
--    }
--
--    bodybuf.flush();
--    request->body = bodybuf.str();
--    bodybuf.str("");
--    return true;
-+    return ready();
-   };
--
--  bool AsyncResponseLoader::feed(const std::string &somedata) {
--    size_t pos;
--    buffer.append(somedata);
--    while(state < 2) {
--      // need to find CRLF in buffer
--      if ((pos = buffer.find("\r\n")) == std::string::npos) return false;
--      std::string line(buffer.begin(), buffer.begin()+pos); // exclude CRLF
--      buffer.erase(buffer.begin(), buffer.begin()+pos+2); // remove line from buffer including CRLF
--      if (state == 0) { // startup line
--        std::string ver;
--        std::istringstream iss(line);
--        iss >> ver >> response->status >> response->statusText;
--        if (ver.find("HTTP/1.") != 0)
--          throw ParseError("Not a HTTP 1.x response");
--        state = 1;
--      } else if (state == 1) {
--        std::string key,value;
--        size_t pos;
--        if (line.empty()) {
--          chunked = (response->headers.find("transfer-encoding") != response->headers.end() && response->headers["transfer-encoding"] == "chunked");
--          state = 2;
--          break;
--        }
--        // split headers
--        if ((pos = line.find_first_of(": ")) == std::string::npos)
--          throw ParseError("Malformed header line");
--        key = line.substr(0, pos);
--        value = line.substr(pos+2);
--        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
--        response->headers[key] = value;
--      }
-+  
-+  void HTTPBase::write(std::ostream& os) const {
-+    if (kind == YAHTTP_TYPE_REQUEST) {
-+      std::ostringstream getparmbuf;
-+      std::string getparms;
-+      // prepare URL 
-+      for(strstr_map_t::const_iterator i = getvars.begin(); i != getvars.end(); i++) {
-+        getparmbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
-+      }
-+      if (getparmbuf.str().length() > 0)  
-+        getparms = "?" + std::string(getparmbuf.str().begin(), getparmbuf.str().end() - 1);
-+      else
-+        getparms = "";
-+      os << method << " " << url.path << getparms << " HTTP/1.1";
-+    } else if (kind == YAHTTP_TYPE_RESPONSE) {
-+      os << "HTTP/1.1 " << status << " ";
-+      if (statusText.empty())
-+        os << Utility::status2text(status);
-+      else
-+        os << statusText;
-     }
--
--    if (buffer.size() == 0) return false;
--      
--    while(buffer.size() > 0) {
--      char buf[1024] = {0};
--
--      if (chunked) {
--        if (chunk_size == 0) {
--          // read chunk length
--          if ((pos = buffer.find('\n')) == std::string::npos) return false;
--          if (pos > 1023) 
--            throw ParseError("Impossible chunk_size");
--          buffer.copy(buf, pos);
--          buf[pos]=0; // just in case...
--          buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
--          sscanf(buf, "%x", &chunk_size);
--          if (!chunk_size) break; // last chunk
-+    os << "\r\n";
-+  
-+    // write headers
-+    strstr_map_t::const_iterator iter = headers.begin();
-+    while(iter != headers.end()) {
-+      if (iter->first == "host" && kind != YAHTTP_TYPE_REQUEST) { iter++; continue; }
-+      os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
-+      iter++;
-+    }
-+    if (jar.cookies.size() > 0) { // write cookies
-+      for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
-+        if (kind == YAHTTP_TYPE_REQUEST) {
-+          os << "Cookie: ";
-         } else {
--          if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
--          if (buffer.at(chunk_size) != '\n') return false; // there should be newline.
--          buffer.copy(buf, chunk_size);
--          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+1);
--          bodybuf << buf;
--          chunk_size = 0;
--          if (buffer.size() == 0) return false; // just in case
-+          os << "Set-Cookie: ";
-         }
--      } else {
--        bodybuf << buffer;
--        buffer = "";
-+        os << i->second.str() << "\r\n";
-       }
-     }
--
--    if (chunk_size!=0) return false; // need more data
--
--    bodybuf.flush();
--    response->body = bodybuf.str();
--    bodybuf.str("");
--    return true;
-+    os << "\r\n";
-+#ifdef HAVE_CPP_FUNC_PTR
-+    this->renderer(this, os);
-+#else
-+    os << body;
-+#endif
-+  };
-+  
-+  std::ostream& operator<<(std::ostream& os, const Response &resp) {
-+    resp.write(os);
-+    return os;
-+  };
-+  
-+  std::istream& operator>>(std::istream& is, Response &resp) {
-+    YaHTTP::AsyncResponseLoader arl;
-+    arl.initialize(&resp);
-+    while(is.good()) {
-+      char buf[1024];
-+      is.read(buf, 1024);
-+      if (is.gcount()) { // did we actually read anything
-+        is.clear();
-+        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
-+      }
-+    }
-+    // throw unless ready
-+    if (arl.ready() == false) 
-+      throw ParseError("Was not able to extract a valid Response from stream");
-+    arl.finalize();
-+    return is;
-+  };
-+  
-+  std::ostream& operator<<(std::ostream& os, const Request &req) {
-+    req.write(os);
-+    return os;
-+  };
-+  
-+  std::istream& operator>>(std::istream& is, Request &req) {
-+    YaHTTP::AsyncRequestLoader arl;
-+    arl.initialize(&req);
-+    while(is.good()) {
-+      char buf[1024];
-+      is.read(buf, 1024);
-+      if (is.gcount()) { // did we actually read anything
-+        is.clear();
-+        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
-+      }
-+    }
-+    if (arl.ready() == false)
-+      throw ParseError("Was not able to extract a valid Request from stream");
-+    arl.finalize();
-+    return is;
-   };
- };
-Index: pdns-recursor/ext/yahttp/yahttp/reqresp.hpp
-===================================================================
---- pdns-recursor.orig/ext/yahttp/yahttp/reqresp.hpp	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ext/yahttp/yahttp/reqresp.hpp	2014-10-21 21:29:06.005312990 +0200
-@@ -1,108 +1,278 @@
--#include <map>
--#include <iostream>
--#include <locale>
--#include <algorithm>
--
--#include "url.hpp"
--#include "utility.hpp"
--#include "exception.hpp"
-+#ifdef HAVE_CXX11
-+#include <functional>
-+#define HAVE_CPP_FUNC_PTR
-+namespace funcptr = std;
-+#else
-+#ifdef HAVE_BOOST
-+#include <boost/function.hpp>
-+namespace funcptr = boost;
-+#define HAVE_CPP_FUNC_PTR
-+#endif
-+#endif
-+
-+#include <fstream>
-+#include <cctype>
-+
-+#ifndef WIN32
-+#include <cstdio>
-+#include <unistd.h>
-+#endif
- 
--#ifndef YAHTTP_MAX_REQUEST_SIZE 
-+#ifndef YAHTTP_MAX_REQUEST_SIZE
- #define YAHTTP_MAX_REQUEST_SIZE 2097152
- #endif
- 
--namespace YaHTTP {
--  typedef std::map<std::string,std::string> strstr_map_t;
-+#ifndef YAHTTP_MAX_RESPONSE_SIZE
-+#define YAHTTP_MAX_RESPONSE_SIZE 2097152
-+#endif
- 
--  class Response;
--  class Request;
--  class AsyncResponseLoader;
--  class AsyncRequestLoader;
-+#define YAHTTP_TYPE_REQUEST 1
-+#define YAHTTP_TYPE_RESPONSE 2
- 
--  class Request {
--  public:
--     Request();
--     Request(const Response &resp);
--     Request(const Request &req);
--     ~Request();
-+namespace YaHTTP {
-+  typedef std::map<std::string,std::string> strstr_map_t; //<! String to String map 
-+  typedef std::map<std::string,Cookie> strcookie_map_t; //<! String to Cookie map
- 
--     void build(const std::string &method, const std::string &url, const std::string &params);
-+  typedef enum {
-+    urlencoded,
-+    multipart
-+  } postformat_t; //<! Enumeration of possible post encodings, url encoding or multipart
- 
--     void load(std::istream &is);
--     void write(std::ostream &os) const;
-+  /*! Base class for request and response */
-+  class HTTPBase {
-+  public:
-+#ifdef HAVE_CPP_FUNC_PTR
-+    /*! Default renderer for request/response, simply copies body to response */
-+    class SendBodyRender {
-+    public:
-+      SendBodyRender() {};
-+
-+      size_t operator()(const HTTPBase *doc, std::ostream& os) const {
-+        os << doc->body;
-+        return doc->body.length();
-+      }; //<! writes body to ostream and returns length 
-+    };
-+    /* Simple sendfile renderer which streams file to ostream */
-+    class SendFileRender {
-+    public:
-+      SendFileRender(const std::string& path) {
-+        this->path = path;
-+      };
-+  
-+      size_t operator()(const HTTPBase *doc __attribute__((unused)), std::ostream& os) const {
-+        char buf[4096];
-+        size_t n,k;
-+#ifdef HAVE_CXX11
-+        std::ifstream ifs(path, std::ifstream::binary);
-+#else
-+        std::ifstream ifs(path.c_str(), std::ifstream::binary);
-+#endif
-+        n = 0;
-+        while(ifs && ifs.good()) {
-+          ifs.read(buf, sizeof buf);
-+          n += (k = ifs.gcount());
-+          if (k)
-+            os.write(buf, k);
-+        }
- 
--     strstr_map_t headers;
--     strstr_map_t parameters;
--     strstr_map_t cookies;
-+        return n;
-+      }; //<! writes file to ostream and returns length
- 
--     URL url;
--     std::string method;
--     std::string body;
-+      std::string path; //<! File to send
-+    };
-+#endif
-+    HTTPBase() {
-+      kind = 0;
-+      status = 0;
-+#ifdef HAVE_CPP_FUNC_PTR
-+      renderer = SendBodyRender();
-+#endif
-+      max_request_size = YAHTTP_MAX_REQUEST_SIZE;
-+      max_response_size = YAHTTP_MAX_RESPONSE_SIZE;
-+    };
-+protected:
-+    HTTPBase(const HTTPBase& rhs) {
-+      this->url = rhs.url; this->kind = rhs.kind;
-+      this->status = rhs.status; this->statusText = rhs.statusText;
-+      this->method = rhs.method; this->headers = rhs.headers;
-+      this->jar = rhs.jar; this->postvars = rhs.postvars;
-+      this->parameters = rhs.parameters; this->getvars = rhs.getvars;
-+      this->body = rhs.body; this->max_request_size = rhs.max_request_size;
-+      this->max_response_size = rhs.max_response_size;
-+#ifdef HAVE_CPP_FUNC_PTR
-+      this->renderer = rhs.renderer;
-+#endif
-+    };
-+    HTTPBase& operator=(const HTTPBase& rhs) {
-+      this->url = rhs.url; this->kind = rhs.kind;
-+      this->status = rhs.status; this->statusText = rhs.statusText;
-+      this->method = rhs.method; this->headers = rhs.headers;
-+      this->jar = rhs.jar; this->postvars = rhs.postvars;
-+      this->parameters = rhs.parameters; this->getvars = rhs.getvars;
-+      this->body = rhs.body; this->max_request_size = rhs.max_request_size;
-+      this->max_response_size = rhs.max_response_size;
-+#ifdef HAVE_CPP_FUNC_PTR
-+      this->renderer = rhs.renderer;
-+#endif
-+      return *this;
-+    };
-+public:
-+    URL url; //<! URL of this request/response
-+    int kind; //<! Type of object (1 = request, 2 = response)
-+    int status; //<! status code 
-+    std::string statusText; //<! textual representation of status code
-+    std::string method; //<! http verb
-+    strstr_map_t headers; //<! map of header(s)
-+    CookieJar jar; //<! cookies 
-+    strstr_map_t postvars; //<! map of POST variables (from POST body)
-+    strstr_map_t getvars; //<! map of GET variables (from URL)
-+// these two are for Router
-+    strstr_map_t parameters; //<! map of route parameters (only if you use YaHTTP::Router)
-+    std::string routeName; //<! name of the current route (only if you use YaHTTP::Router)
-+
-+    std::string body; //<! the actual content
-+
-+    ssize_t max_request_size; //<! maximum size of request
-+    ssize_t max_response_size;  //<! maximum size of response
-+ 
-+#ifdef HAVE_CPP_FUNC_PTR
-+    funcptr::function<size_t(const HTTPBase*,std::ostream&)> renderer; //<! rendering function
-+#endif
-+    void write(std::ostream& os) const; //<! writes request to the given output stream
- 
--     friend std::istream& operator>>(std::istream& os, const Request &req);
--     friend std::ostream& operator<<(std::ostream& os, const Request &req);
--     friend class AsyncRequestLoader;
-+    strstr_map_t& GET() { return getvars; }; //<! acccessor for getvars
-+    strstr_map_t& POST() { return postvars; }; //<! accessor for postvars
-+    strcookie_map_t& COOKIES() { return jar.cookies; }; //<! accessor for cookies
-+
-+    std::string str() const {
-+       std::ostringstream oss;
-+       write(oss);
-+       return oss.str();
-+    }; //<! return string representation of this object
-   };
- 
--  class Response {
-+  /*! Response class, represents a HTTP Response document */
-+  class Response: public HTTPBase { 
-   public:
--     Response();
--     Response(const Request &req);
--     Response(const Response &resp);
--     ~Response();
--     void load(std::istream &is);
--     void write(std::ostream &os) const;
--
--     strstr_map_t headers;
--     strstr_map_t parameters;
--     strstr_map_t cookies;
--
--     URL url;
--     int status;
--     std::string statusText;
--     std::string method;
--     std::string body;
--
--     friend std::istream& operator>>(std::istream& is, Response &resp);
--     friend std::ostream& operator<<(std::ostream& os, const Response &resp);
--     friend class AsyncResponseLoader;
-+    Response() { this->kind = YAHTTP_TYPE_RESPONSE; };
-+    Response(const HTTPBase& rhs): HTTPBase(rhs) {
-+      this->kind = YAHTTP_TYPE_RESPONSE;
-+    };
-+    Response& operator=(const HTTPBase& rhs) {
-+      HTTPBase::operator=(rhs);
-+      this->kind = YAHTTP_TYPE_RESPONSE;
-+      return *this;
-+    };
-+    friend std::ostream& operator<<(std::ostream& os, const Response &resp);
-+    friend std::istream& operator>>(std::istream& is, Response &resp);
-   };
- 
--  class AsyncResponseLoader {
-+  /* Request class, represents a HTTP Request document */
-+  class Request: public HTTPBase {
-   public:
--    AsyncResponseLoader(Response *response) {
--      state = 0;
--      chunked = false;
--      chunk_size = 0;
--      this->response = response;
--    };
--    bool feed(const std::string &somedata);
--  private:
--    Response *response;
--    int state;
--    std::string buffer;
--    bool chunked;
--    int chunk_size;
--    std::ostringstream bodybuf;
-+    Request() { this->kind = YAHTTP_TYPE_REQUEST; };
-+    Request(const HTTPBase& rhs): HTTPBase(rhs) {
-+      this->kind = YAHTTP_TYPE_REQUEST;
-+    };
-+    Request& operator=(const HTTPBase& rhs) {
-+      HTTPBase::operator=(rhs);
-+      this->kind = YAHTTP_TYPE_REQUEST;
-+      return *this;
-+    };
-+
-+    void setup(const std::string& method, const std::string& url) {
-+      this->url.parse(url);
-+      this->headers["host"] = this->url.host;
-+      this->method = method;
-+      std::transform(this->method.begin(), this->method.end(), this->method.begin(), ::toupper);
-+      this->headers["user-agent"] = "YaHTTP v1.0";
-+    }; //<! Set some initial things for a request
-+
-+    void preparePost(postformat_t format = urlencoded) {
-+      std::ostringstream postbuf;
-+      if (format == urlencoded) {
-+        for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
-+          postbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
-+        }
-+        // remove last bit
-+        if (postbuf.str().length()>0) 
-+          body = postbuf.str().substr(0, postbuf.str().length()-1);
-+        else
-+          body = "";
-+        headers["content-type"] = "application/x-www-form-urlencoded; charset=utf-8";
-+      } else if (format == multipart) {
-+        headers["content-type"] = "multipart/form-data; boundary=YaHTTP-12ca543";
-+        for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
-+          postbuf << "--YaHTTP-12ca543\r\nContent-Disposition: form-data; name=\"" << Utility::encodeURL(i->first, false) << "; charset=UTF-8\r\n\r\n"
-+            << Utility::encodeURL(i->second, false) << "\r\n";
-+        }
-+      }
-+
-+      postbuf.str("");
-+      postbuf << body.length();
-+      // set method and change headers
-+      method = "POST";
-+      headers["content-length"] = postbuf.str();
-+    }; //<! convert all postvars into string and stuff it into body
-+
-+    friend std::ostream& operator<<(std::ostream& os, const Request &resp);
-+    friend std::istream& operator>>(std::istream& is, Request &resp);
-   };
- 
--  class AsyncRequestLoader {
-+  /*! Asynchronous HTTP document loader */
-+  template <class T>
-+  class AsyncLoader {
-   public:
--    AsyncRequestLoader(Request *request) {
--      state = 0;
--      chunked = false;
--      chunk_size = 0;
--      maxbody = 0;
--      this->request = request;
--    };
--    bool feed(const std::string &somedata);
--  private:
--    Request *request;
--    int state;
--    std::string buffer;
--    bool chunked;
--    int chunk_size;
--    std::ostringstream bodybuf;
--    long maxbody;
-+    T* target; //<! target to populate
-+    int state; //<! reader state
-+    size_t pos; //<! reader position
-+    
-+    std::string buffer; //<! read buffer 
-+    bool chunked; //<! whether we are parsing chunked data
-+    int chunk_size; //<! expected size of next chunk
-+    std::ostringstream bodybuf; //<! buffer for body
-+    size_t maxbody; //<! maximum size of body
-+    size_t minbody; //<! minimum size of body
-+    bool hasBody; //<! are we expecting body
-+
-+    void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value); //<! key value pair parser helper
-+
-+    void initialize(T* target) {
-+      chunked = false; chunk_size = 0;
-+      bodybuf.str(""); maxbody = 0;
-+      pos = 0; state = 0; this->target = target; 
-+      hasBody = false;
-+    }; //<! Initialize the parser for target and clear state
-+    int feed(const std::string& somedata); //<! Feed data to the parser
-+    bool ready() {
-+     return (chunked == true && state == 3) || // if it's chunked we get end of data indication
-+             (chunked == false && state > 1 &&  
-+               (!hasBody || 
-+                 (bodybuf.str().size() <= maxbody && 
-+                  bodybuf.str().size() >= minbody)
-+               )
-+             ); 
-+    }; //<! whether we have received enough data
-+    void finalize() {
-+      bodybuf.flush();
-+      if (ready()) {
-+        strstr_map_t::iterator pos = target->headers.find("content-type");
-+        if (pos != target->headers.end() && Utility::iequals(pos->second, "application/x-www-form-urlencoded", 32)) {
-+          target->postvars = Utility::parseUrlParameters(bodybuf.str());
-+        }
-+        target->body = bodybuf.str();
-+      }
-+      bodybuf.str("");
-+      this->target = NULL;
-+    }; //<! finalize and release target
-+  };
-+
-+  /*! Asynchronous HTTP response loader */
-+  class AsyncResponseLoader: public AsyncLoader<Response> {
-+  };
-+
-+  /*! Asynchronous HTTP request loader */
-+  class AsyncRequestLoader: public AsyncLoader<Request> {
-   };
-+
- };
-Index: pdns-recursor/ext/yahttp/yahttp/router.cpp
-===================================================================
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ pdns-recursor/ext/yahttp/yahttp/router.cpp	2014-10-21 21:29:06.005312990 +0200
-@@ -0,0 +1,163 @@
-+/* @file
-+ * @brief Concrete implementation of Router 
-+ */
-+#include "yahttp.hpp"
-+#include "router.hpp"
-+
-+namespace YaHTTP {
-+  typedef funcptr::tuple<int,int> TDelim;
-+
-+  // router is defined here.
-+  YaHTTP::Router Router::router;
-+
-+  void Router::map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name) {
-+    std::string method2 = method;
-+    bool isopen=false;
-+    // add into vector
-+    for(std::string::const_iterator i = url.begin(); i != url.end(); i++) {
-+       if (*i == '<' && isopen) throw Error("Invalid URL mask, cannot have < after <");
-+       if (*i == '<') isopen = true;
-+       if (*i == '>' && !isopen) throw Error("Invalid URL mask, cannot have > without < first");
-+       if (*i == '>') isopen = false;
-+    }
-+    std::transform(method2.begin(), method2.end(), method2.begin(), ::toupper); 
-+    routes.push_back(funcptr::make_tuple(method2, url, handler, name));
-+  };
-+
-+  bool Router::route(Request *req, THandlerFunction& handler) {
-+    std::map<std::string, TDelim> params;
-+    int pos1,pos2;
-+    std::string pname;
-+    bool matched = false;
-+    std::string rname;
-+
-+    // iterate routes
-+    for(TRouteList::iterator i = routes.begin(); !matched && i != routes.end(); i++) {
-+      int k1,k2,k3;
-+      std::string pname;
-+      std::string method, url;
-+      funcptr::tie(method, url, handler, rname) = *i;
-+    
-+      if (method.empty() == false && req->method != method) continue; // no match on method
-+      // see if we can't match the url
-+      params.clear();
-+      // simple matcher func
-+      for(k1=0, k2=0; k1 < static_cast<int>(url.size()) && k2 < static_cast<int>(req->url.path.size()); ) {
-+        if (url[k1] == '<') {
-+          pos1 = k2;
-+          k3 = k1+1;
-+          // start of parameter
-+          while(k1 < static_cast<int>(url.size()) && url[k1] != '>') k1++;
-+          pname = std::string(url.begin()+k3, url.begin()+k1);
-+          // then we also look it on the url
-+          if (pname[0]=='*') {
-+            pname = pname.substr(1);
-+            // this matches whatever comes after it, basically end of string
-+            pos2 = req->url.path.size();
-+            matched = true;
-+            if (pname != "") 
-+              params[pname] = funcptr::tie(pos1,pos2);
-+            k1 = url.size();
-+            k2 = req->url.path.size();
-+            break;
-+          } else { 
-+            // match until url[k1]
-+            while(k2 < static_cast<int>(req->url.path.size()) && req->url.path[k2] != url[k1+1]) k2++;
-+            pos2 = k2;
-+            params[pname] = funcptr::tie(pos1,pos2);
-+          }
-+          k2--;
-+        }
-+        else if (url[k1] != req->url.path[k2]) {
-+          break;
-+        }
-+
-+        k1++; k2++;
-+      }
-+
-+      // ensure.
-+      if (url[k1] != req->url.path[k2]) 
-+        matched = false;
-+      else
-+        matched = true;
-+    }
-+
-+    if (!matched) { return false; } // no route
-+    req->parameters.clear();    
-+
-+    for(std::map<std::string, TDelim>::iterator i = params.begin(); i != params.end(); i++) {
-+      int p1,p2;
-+      funcptr::tie(p1,p2) = i->second;
-+      std::string value(req->url.path.begin() + p1, req->url.path.begin() + p2);
-+      value = Utility::decodeURL(value);
-+      req->parameters[i->first] = value;
-+    }
-+
-+    req->routeName = rname;
-+
-+    return true;
-+  };
-+
-+  void Router::printRoutes(std::ostream &os) {
-+    for(TRouteList::iterator i = routes.begin(); i != routes.end(); i++) {
-+#ifdef HAVE_CXX11
-+      std::streamsize ss = os.width();
-+      std::ios::fmtflags ff = os.setf(std::ios::left);
-+      os.width(10);
-+      os << std::get<0>(*i);
-+      os.width(50);
-+      os << std::get<1>(*i);
-+      os.width(ss);
-+      os.setf(ff);
-+      os << "    " << std::get<3>(*i);
-+      os << std::endl;
-+#else
-+      os << i->get<0>() << "    " << i->get<1>() << "    " << i->get<3>() << std::endl;
-+#endif
-+    } 
-+  };
-+
-+  std::pair<std::string,std::string> Router::urlFor(const std::string &name, const strstr_map_t& arguments) {
-+    std::ostringstream path;
-+    std::string mask,method,result;
-+    int k1,k2,k3;
-+
-+    bool found = false;
-+    for(TRouteList::iterator i = routes.begin(); !found && i != routes.end(); i++) {
-+#ifdef HAVE_CXX11
-+      if (std::get<3>(*i) == name) { mask = std::get<1>(*i); method = std::get<0>(*i); found = true; }
-+#else
-+      if (i->get<3>() == name) { mask = i->get<1>(); method = i->get<0>(); found = true; }
-+#endif
-+    }
-+
-+    if (!found)
-+      throw Error("Route not found");
-+
-+    for(k1=0,k3=0;k1<static_cast<int>(mask.size());k1++) {
-+      if (mask[k1] == '<') {
-+        std::string pname;
-+        strstr_map_t::const_iterator pptr;
-+        k2=k1;
-+        while(k1<static_cast<int>(mask.size()) && mask[k1]!='>') k1++;
-+        path << mask.substr(k3,k2-k3);
-+        if (mask[k2+1] == '*')
-+          pname = std::string(mask.begin() + k2 + 2, mask.begin() + k1);
-+        else 
-+          pname = std::string(mask.begin() + k2 + 1, mask.begin() + k1);
-+        if ((pptr = arguments.find(pname)) != arguments.end()) 
-+          path << Utility::encodeURL(pptr->second);
-+        k3 = k1+1;
-+      }
-+      else if (mask[k1] == '*') {
-+        // ready 
-+        k3++;
-+        continue;
-+      }
-+    }
-+    std::cout << mask.substr(k3) << std::endl;
-+    path << mask.substr(k3);
-+    result = path.str();
-+    return std::make_pair(method, result);
-+  }
-+};
-Index: pdns-recursor/ext/yahttp/yahttp/router.hpp
-===================================================================
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ pdns-recursor/ext/yahttp/yahttp/router.hpp	2014-10-21 21:29:06.005312990 +0200
-@@ -0,0 +1,74 @@
-+#ifndef _YAHTTP_ROUTER_HPP
-+#define _YAHTTP_ROUTER_HPP 1
-+/* @file 
-+ * @brief Defines router class and support structures
-+ */
-+#ifdef HAVE_CXX11
-+#include <functional>
-+#include <tuple>
-+#define HAVE_CPP_FUNC_PTR
-+#define IGNORE std::ignore
-+namespace funcptr = std;
-+#else
-+#ifdef HAVE_BOOST
-+#include <boost/function.hpp>
-+#include <boost/tuple/tuple.hpp>
-+#define IGNORE boost::tuples::ignore
-+namespace funcptr = boost;
-+#define HAVE_CPP_FUNC_PTR
-+#else
-+#warn "You need to configure with boost or have C++11 capable compiler for router"
-+#endif
-+#endif
-+
-+#ifdef HAVE_CPP_FUNC_PTR
-+#include <vector>
-+#include <utility>
-+
-+namespace YaHTTP {
-+  typedef funcptr::function <void(Request* req, Response* resp)> THandlerFunction; //!< Handler function pointer 
-+  typedef funcptr::tuple<std::string, std::string, THandlerFunction, std::string> TRoute; //!< Route tuple (method, urlmask, handler, name)
-+  typedef std::vector<TRoute> TRouteList; //!< List of routes in order of evaluation
-+
-+  /*! Implements simple router.
-+
-+This class implements a router for masked urls. The URL mask syntax is as of follows
-+
-+/&lt;masked&gt;/url&lt;number&gt;/&lt;hi&gt;.&lt;format&gt;
-+
-+You can use &lt;*param&gt; to denote that everything will be matched and consumed into the parameter, including slash (/). Use &lt;*&gt; to denote that URL 
-+is consumed but not stored. Note that only path is matched, scheme, host and url parameters are ignored. 
-+   */
-+  class Router {
-+  private:
-+    Router() {}; 
-+    static Router router; //<! Singleton instance of Router
-+  public:
-+    void map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name); //<! Instance method for mapping urls
-+    bool route(Request *req, THandlerFunction& handler); //<! Instance method for performing routing
-+    void printRoutes(std::ostream &os); //<! Instance method for printing routes
-+    std::pair<std::string, std::string> urlFor(const std::string &name, const strstr_map_t& arguments); //<! Instance method for generating paths
-+
-+/*! Map an URL.
-+If method is left empty, it will match any method. Name is also optional, but needed if you want to find it for making URLs 
-+*/
-+    static void Map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map(method, url, handler, name); }; 
-+    static void Get(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("GET", url, handler, name); }; //<! Helper for mapping GET
-+    static void Post(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("POST", url, handler, name); }; //<! Helper for mapping POST
-+    static void Put(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("PUT", url, handler, name); }; //<! Helper for mapping PUT
-+    static void Patch(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("PATCH", url, handler, name); }; //<! Helper for mapping PATCH
-+    static void Delete(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("DELETE", url, handler, name); }; //<! Helper for mapping DELETE
-+    static void Any(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("", url, handler, name); }; //<! Helper for mapping any method
-+
-+    static bool Route(Request *req, THandlerFunction& handler) { return router.route(req, handler); }; //<! Performs routing based on req->url.path 
-+    static void PrintRoutes(std::ostream &os) { router.printRoutes(os); }; //<! Prints all known routes to given output stream
-+
-+    static std::pair<std::string, std::string> URLFor(const std::string &name, const strstr_map_t& arguments) { return router.urlFor(name,arguments); }; //<! Generates url from named route and arguments. Missing arguments are assumed empty
-+    static const TRouteList& GetRoutes() { return router.routes; } //<! Reference to route list 
-+
-+    TRouteList routes; //<! Instance variable for routes
-+  };
-+};
-+#endif
-+
-+#endif
-Index: pdns-recursor/ext/yahttp/yahttp/url.hpp
-===================================================================
---- pdns-recursor.orig/ext/yahttp/yahttp/url.hpp	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ext/yahttp/yahttp/url.hpp	2014-10-21 21:29:06.005312990 +0200
-@@ -10,6 +10,7 @@
- #endif 
- 
- namespace YaHTTP {
-+  /*! URL parser and container */
-   class URL {
-    private: 
-       bool parseSchema(const std::string& url, size_t &pos) {
-@@ -25,7 +26,7 @@ namespace YaHTTP {
-              pos += 2;
-           }
-           return true;
--      }
-+      }; //<! parse schema/protocol part 
- 
-       bool parseHost(const std::string& url, size_t &pos) {
-           size_t pos1;
-@@ -38,8 +39,13 @@ namespace YaHTTP {
-              host = url.substr(pos, pos1-pos);
-              pos = pos1;
-           }
-+          if ( (pos1 = host.find_first_of(":")) != std::string::npos ) {
-+             std::istringstream tmp(host.substr(pos1+1));
-+             tmp >> port;
-+             host = host.substr(0, pos1);
-+          }
-           return true;
--      }
-+      }; //<! parse host and port
- 
-       bool parseUserPass(const std::string& url, size_t &pos) {
-           size_t pos1,pos2;
-@@ -58,22 +64,21 @@ namespace YaHTTP {
-           pos = pos1+1;
-           username = Utility::decodeURL(username);
-           return true;
--      };
-+      }; //<! parse possible username and password
- 
-       bool parsePath(const std::string& url, size_t &pos) {
-           size_t pos1;
-           if (pos >= url.size()) return true; // no data
-           if (url[pos] != '/') return false; // not an url
-           if ( (pos1 = url.find_first_of("?", pos)) == std::string::npos ) {
--             path = url;
-+             path = url.substr(pos);
-              pos = url.size();
-           } else {
-              path = url.substr(pos, pos1-pos);
-              pos = pos1;
-           }
--          path = Utility::decodeURL(path);
-           return true;
--      }
-+      }; //<! parse path component
- 
-       bool parseParameters(const std::string& url, size_t &pos) {
-           size_t pos1;
-@@ -87,15 +92,20 @@ namespace YaHTTP {
-              parameters = url.substr(pos+1, pos1-pos-1);
-              pos = pos1;
-           }
-+          if (parameters.size()>0 && *(parameters.end()-1) == '&') parameters.resize(parameters.size()-1);
-           return true;
--      }
-+      }; //<! parse url parameters
- 
-       bool parseAnchor(const std::string& url, size_t &pos) {
-           if (pos >= url.size()) return true; // no data
-           if (url[pos] != '#') return false; // not anchor
-           anchor = url.substr(pos+1);
-           return true;
--      }
-+      }; //<! parse anchor
-+
-+      void initialize() {
-+        protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true;
-+      }; //<! initialize to empty URL
- 
-   public:
-       std::string to_string() const {
-@@ -122,7 +132,7 @@ namespace YaHTTP {
-               port > 0) 
-             oss << ":" << port;
- 
--          oss << Utility::encodeURL(path, true);
-+          oss << path;
-           if (parameters.empty() == false) {
-              if (!pathless) 
-                 oss << "?";
-@@ -131,53 +141,51 @@ namespace YaHTTP {
-           if (anchor.empty() == false)
-              oss << "#" << anchor;
-           return oss.str();
--      }
-+      }; //<! convert this URL to string
- 
--      std::string protocol;
--      std::string host;
--      int port;
--      std::string username;
--      std::string password;
--      std::string path;
--      std::string parameters;
--      std::string anchor;
--      bool pathless;
-+      std::string protocol; //<! schema/protocol 
-+      std::string host; //<! host
-+      int port; //<! port
-+      std::string username; //<! username
-+      std::string password; //<! password
-+      std::string path; //<! path 
-+      std::string parameters; //<! url parameters
-+      std::string anchor; //<! anchor
-+      bool pathless; //<! whether this url has no path
- 
--      URL() { protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true; };
-+      URL() { initialize(); }; //<! construct empty url
-       URL(const std::string& url) {
--          parse(url);
--      };
-+        parse(url);
-+      }; //<! calls parse with url 
- 
-       URL(const char *url) {
--          parse(std::string(url));
--      };
-+        parse(std::string(url));
-+      }; //<! calls parse with url
- 
-       bool parse(const std::string& url) {
--          // setup
--          protocol = ""; host = ""; port = 0; 
--          username = ""; password = ""; path = ""; 
--          parameters = ""; anchor =""; pathless = true;
--
--          if (url.size() > YAHTTP_MAX_URL_LENGTH) return false;
--          size_t pos = 0;
--          if (*(url.begin()) != '/') { // full url?
--             if (parseSchema(url, pos) == false) return false;
--             if (pathless) {
--                parameters = url.substr(pos);
--                return true;
--             }
--             if (parseUserPass(url, pos) == false) return false;
--             if (parseHost(url, pos) == false) return false;
--          }
--          if (parsePath(url, pos) == false) return false;
--          if (parseParameters(url, pos) == false) return false;
--          return parseAnchor(url, pos);
--      };
--
--      friend std::ostream & operator<<(std::ostream& os, const URL& url) {
--         os<<url.to_string();
--         return os;
--      }
-+        // setup
-+        initialize();
-+
-+        if (url.size() > YAHTTP_MAX_URL_LENGTH) return false;
-+        size_t pos = 0;
-+        if (*(url.begin()) != '/') { // full url?
-+          if (parseSchema(url, pos) == false) return false;
-+          if (pathless) {
-+            parameters = url.substr(pos);
-+            return true;
-+          }
-+          if (parseUserPass(url, pos) == false) return false;
-+          if (parseHost(url, pos) == false) return false;
-+        }
-+        if (parsePath(url, pos) == false) return false;
-+        if (parseParameters(url, pos) == false) return false;
-+        return parseAnchor(url, pos);
-+    }; //<! parse various formats of urls ranging from http://example.com/foo?bar=baz into data:base64:d089swt64wt... 
-+
-+    friend std::ostream & operator<<(std::ostream& os, const URL& url) {
-+      os<<url.to_string();
-+      return os;
-+    };
-   };
- };
- #endif
-Index: pdns-recursor/ext/yahttp/yahttp/utility.hpp
-===================================================================
---- pdns-recursor.orig/ext/yahttp/yahttp/utility.hpp	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ext/yahttp/yahttp/utility.hpp	2014-10-21 21:29:06.005312990 +0200
-@@ -1,11 +1,189 @@
- #ifndef _YAHTTP_UTILITY_HPP
- #define _YAHTTP_UTILITY_HPP 1
- 
--#include <string>
--#include <algorithm>
--#include <cstdio>
--
- namespace YaHTTP {
-+  static const char *MONTHS[] = {0,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",0}; //<! List of months 
-+  static const char *DAYS[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat",0}; //<! List of days
-+
-+  /*! Represents a date/time with utc offset */
-+  class DateTime {
-+  public:
-+     bool isSet; //<! if this is initialized yet
-+
-+     int year; //<! year, 0 is year 0, not 1900
-+
-+     int month; //<! month, range 1-12
-+     int day; //<! day, range 1-31
-+     int wday; //<! week day, range 1-7
-+
-+     int hours; //<! hours, range 0-23
-+     int minutes; //<! minutes, range 0-59
-+     int seconds; //<! seconds, range 0-60
-+
-+     int utc_offset; //<! UTC offset with minutes (hhmm)
-+
-+     DateTime() { 
-+       initialize();
-+     }; //<! Construct and initialize
-+
-+     void initialize() {
-+       isSet = false; 
-+       year = month = day = wday = hours = minutes = seconds = utc_offset = 0;
-+       month = 1; // it's invalid otherwise
-+     }; //<! Creates year 0 date
-+
-+     void setLocal() {
-+       fromLocaltime(time((time_t*)NULL)); 
-+     }; //<! sets current local time
-+
-+     void setGm() {
-+       fromGmtime(time((time_t*)NULL));
-+     }; //<! sets current gmtime (almost UTC)
-+
-+     void fromLocaltime(time_t t) {
-+#ifdef HAVE_LOCALTIME_R
-+       struct tm tm;
-+       localtime_r(&t, &tm);
-+       fromTm(&tm);
-+#else
-+       struct tm *tm;
-+       tm = localtime(&t);
-+       fromTm(tm);
-+#endif
-+#ifndef HAVE_TM_GMTOFF
-+       time_t t2;
-+# ifdef HAVE_LOCALTIME_R
-+       gmtime_r(&t, &tm);
-+       t2 = mktime(&tm);
-+# else
-+       tm = gmtime(&t);
-+       t2 = mktime(tm);
-+# endif
-+       this->utc_offset = ((t2-t)/10)*10; // removes any possible differences. 
-+#endif
-+     }; //<! uses localtime for time
-+
-+     void fromGmtime(time_t t) {
-+#ifdef HAVE_GMTIME_R
-+       struct tm tm;
-+       gmtime_r(&t, &tm);
-+       fromTm(&tm);
-+#else
-+       struct tm *tm;
-+       tm = gmtime(&t);
-+       fromTm(tm);
-+#endif
-+#ifndef HAVE_TM_GMTOFF
-+       this->utc_offset = 0;
-+#endif
-+     }; //<! uses gmtime for time
-+
-+     void fromTm(const struct tm *tm) {
-+       year = tm->tm_year + 1900;
-+       month = tm->tm_mon + 1;
-+       day = tm->tm_mday;
-+       hours = tm->tm_hour;
-+       minutes = tm->tm_min;
-+       seconds = tm->tm_sec;
-+       wday = tm->tm_wday;
-+#ifdef HAVE_TM_GMTOFF
-+       utc_offset = tm->tm_gmtoff;
-+#endif
-+       isSet = true;
-+     }; //<! parses date from struct tm 
-+
-+     void validate() const {
-+       if (wday < 0 || wday > 6) throw "Invalid date";
-+       if (month < 1 || month > 12) throw "Invalid date";
-+       if (year < 0) throw "Invalid date";
-+       if (hours < 0 || hours > 23 ||
-+           minutes < 0 || minutes > 59 ||
-+           seconds < 0 || seconds > 60) throw "Invalid date";
-+     }; //<! make sure we are within ranges (not a *REAL* validation, just range check)
-+
-+     std::string rfc_str() const {
-+       std::ostringstream oss;
-+       validate();
-+       oss << DAYS[wday] << ", " << std::setfill('0') << std::setw(2) << day << " " << MONTHS[month] << " " <<
-+          std::setfill('0') << std::setw(2) <<  year << " " << 
-+          std::setfill('0') << std::setw(2) << hours << ":" << 
-+          std::setfill('0') << std::setw(2) << minutes << ":" << 
-+          std::setfill('0') << std::setw(2) << seconds << " ";
-+       if (utc_offset>=0) oss << "+";
-+       else oss << "-";
-+       int tmp_off = ( utc_offset < 0 ? utc_offset*-1 : utc_offset ); 
-+       oss << std::setfill('0') << std::setw(2) << (tmp_off/3600);
-+       oss << std::setfill('0') << std::setw(2) << (tmp_off%3600)/60;
-+
-+       return oss.str(); 
-+     }; //<! converts this date into a RFC-822 format
-+ 
-+     std::string cookie_str() const {
-+       std::ostringstream oss;
-+       validate();
-+       oss << std::setfill('0') << std::setw(2) << day << "-" << MONTHS[month] << "-" << year << " " <<
-+         std::setfill('0') << std::setw(2) << hours << ":" << 
-+         std::setfill('0') << std::setw(2) << minutes << ":" << 
-+         std::setfill('0') << std::setw(2) << seconds << " GMT";
-+       return oss.str();
-+     }; //<! converts this date into a HTTP Cookie date
-+ 
-+     void parse822(const std::string &rfc822_date) {
-+       struct tm tm;
-+       const char *ptr;
-+#ifdef HAVE_TM_GMTOFF
-+       if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T %z", &tm)) != NULL) {
-+#else
-+	if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T", &tm)) != NULL) {
-+          int sign;
-+  	  // parse the timezone parameter
-+          while(*ptr && ::isspace(*ptr)) ptr++;
-+          if (*ptr == '+') sign = 0;
-+          else if (*ptr == '-') sign = -1;
-+          else throw "Unparseable date";
-+          ptr++;
-+          utc_offset = ::atoi(ptr) * sign;
-+          while(*ptr && ::isdigit(*ptr)) ptr++;
-+#endif
-+          while(*ptr && ::isspace(*ptr)) ptr++;
-+          if (*ptr) throw "Unparseable date"; // must be final.
-+          fromTm(&tm);
-+       } else {
-+          throw "Unparseable date";
-+       }
-+     }; //<! parses RFC-822 date
-+
-+     void parseCookie(const std::string &cookie_date) {
-+       struct tm tm;
-+       const char *ptr;
-+       if ( (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T", &tm)) != NULL) {
-+          while(*ptr && ( ::isspace(*ptr) || ::isalnum(*ptr) )) ptr++;
-+          std::cerr << ptr << std::endl;
-+          if (*ptr) throw "Unparseable date (non-final)"; // must be final.
-+          fromTm(&tm);
-+          this->utc_offset = 0;
-+       } else {
-+          throw "Unparseable date (did not match pattern cookie)";
-+       }
-+     }; //<! parses HTTP Cookie date
-+
-+     time_t unixtime() const {
-+       struct tm tm;
-+       tm.tm_year = year-1900;
-+       tm.tm_mon = month-1;
-+       tm.tm_mday = day;
-+       tm.tm_hour = hours;
-+       tm.tm_min = minutes;
-+       tm.tm_sec = seconds;
-+       tm.tm_isdst = 0;
-+#ifdef HAVE_TM_GMTOFF
-+       tm.tm_gmtoff = utc_offset;
-+#endif
-+       return mktime(&tm);
-+     }; //<! returns this datetime as unixtime. will not work for dates before 1970/1/1 00:00:00 GMT
-+  };
-+
-+  /*! Various helpers needed in the code */ 
-   class Utility {
-   public:
-     static std::string decodeURL(const std::string& component) {
-@@ -35,23 +213,40 @@ namespace YaHTTP {
-            pos2=pos1;
-         }
-         return result;
--    };
-+    }; //<! Decodes %xx from string into bytes
-     
--    static std::string encodeURL(const std::string& component, bool encodeSlash = true) {
-+    static std::string encodeURL(const std::string& component, bool asUrl = true) {
-       std::string result = component;
-+      std::string skip = "+-.:,&;_#%[]?/@(){}=";
-       char repl[3];
-       size_t pos;
-       for(std::string::iterator iter = result.begin(); iter != result.end(); iter++) {
--        if (*iter != '+' && !(encodeSlash == false || *iter == '/') && !std::isalnum(*iter)) {
-+        if (!std::isalnum(*iter) && (!asUrl || skip.find(*iter) == std::string::npos)) {
-           // replace with different thing
-           pos = std::distance(result.begin(), iter);
--          ::snprintf(repl,3,"%02x", *iter);
-+          ::snprintf(repl,3,"%02x", static_cast<unsigned char>(*iter));
-           result = result.replace(pos, 1, "%", 1).insert(pos+1, repl, 2);
-           iter = result.begin() + pos + 2;
-         }
-       }
-       return result;
--    };
-+    }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url
-+
-+    static std::string encodeURL(const std::wstring& component, bool asUrl = true) {
-+      unsigned char const *p = reinterpret_cast<unsigned char const*>(&component[0]);
-+      std::size_t s = component.size() * sizeof((*component.begin()));
-+      std::vector<unsigned char> vec(p, p+s);
-+
-+      std::ostringstream result;
-+      std::string skip = "+-.,&;_#%[]?/@(){}=";
-+      for(std::vector<unsigned char>::iterator iter = vec.begin(); iter != vec.end(); iter++) {
-+        if (!std::isalnum((char)*iter) && (!asUrl || skip.find((char)*iter) == std::string::npos)) {
-+          // bit more complex replace
-+          result << "%" << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(*iter);
-+        } else result << (char)*iter;
-+      }
-+      return result.str();
-+    }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url, for wide strings, returns ordinary string
- 
-     static std::string status2text(int status) {
-        switch(status) {
-@@ -134,7 +329,7 @@ namespace YaHTTP {
-        default:
-            return "Unknown Status";
-        }
--    };
-+    }; //<! static HTTP codes to text mappings
- 
-     static std::map<std::string,std::string> parseUrlParameters(std::string parameters) {
-       std::string::size_type pos = 0;
-@@ -173,14 +368,33 @@ namespace YaHTTP {
-         pos = nextpos+1;
-       }
-       return parameter_map;
--    };
-+    }; //<! parses URL parameters into string map 
-+
-+    static bool iequals(const std::string& a, const std::string& b, size_t length) {
-+      std::string::const_iterator ai, bi;
-+      size_t i;
-+      for(ai = a.begin(), bi = b.begin(), i = 0; ai != a.end() && bi != b.end() && i < length; ai++,bi++,i++) {
-+        if (::toupper(*ai) != ::toupper(*bi)) return false;
-+      }
-+
-+      if (ai == a.end() && bi == b.end()) return true;
-+      if ((ai == a.end() && bi != b.end()) ||
-+          (ai != a.end() && bi == b.end())) return false;
-+      
-+      return ::toupper(*ai) == ::toupper(*bi);
-+    }; //<! case-insensitive comparison with length
-+
-+    static bool iequals(const std::string& a, const std::string& b) {
-+      if (a.size() != b.size()) return false;
-+      return iequals(a,b,a.size());
-+    }; //<! case-insensitive comparison
- 
--    static void trim_right(std::string &str) {
-+    static void trimRight(std::string &str) {
-        const std::locale &loc = std::locale::classic();
-        std::string::reverse_iterator iter = str.rbegin();
-        while(iter != str.rend() && std::isspace(*iter, loc)) iter++;
-        str.erase(iter.base(), str.end());
--    };
-+    }; //<! removes whitespace from right
- 
-     static std::string camelizeHeader(const std::string &str) {
-        std::string::const_iterator iter = str.begin();
-@@ -198,7 +412,7 @@ namespace YaHTTP {
-        }
- 
-        return result;
--     };
--   };
-+   }; //<! camelizes headers, such as, content-type => Content-Type
-+  };
- };
- #endif
-Index: pdns-recursor/ext/yahttp/yahttp/yahttp-config.h
-===================================================================
---- /dev/null	1970-01-01 00:00:00.000000000 +0000
-+++ pdns-recursor/ext/yahttp/yahttp/yahttp-config.h	2014-10-21 21:29:06.005312990 +0200
-@@ -0,0 +1 @@
-+#include "../../../config.h"
-Index: pdns-recursor/ext/yahttp/yahttp/yahttp.hpp
-===================================================================
---- pdns-recursor.orig/ext/yahttp/yahttp/yahttp.hpp	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ext/yahttp/yahttp/yahttp.hpp	2014-10-21 21:29:06.005312990 +0200
-@@ -1,3 +1,36 @@
-+#include <map>
-+#include <iostream>
-+#include <locale>
-+#include <algorithm>
-+#include <string>
-+#include <cstdio>
-+#include <sys/time.h>
-+#include <iomanip>
-+#include <list>
-+#include <vector>
-+
-+#include "yahttp-config.h"
-+#include "url.hpp"
- #include "utility.hpp"
-+#include "exception.hpp"
- #include "url.hpp"
-+#include "cookie.hpp"
- #include "reqresp.hpp"
-+
-+/*! \mainpage Yet Another HTTP Library Documentation
-+\section sec_quick_start Quick start example
-+
-+@code
-+#include <yahttp/yahttp.hpp>
-+
-+int main(void) {
-+  std::ifstream ifs("request.txt");
-+  YaHTTP::Request req;
-+  ifs >> req;
-+
-+  std::cout << req.method " " << req.url.path << std::endl;
-+  return 0;
-+}
-+@endcode
-+\author Aki Tuomi
-+*/
-Index: pdns-recursor/Makefile
-===================================================================
---- pdns-recursor.orig/Makefile	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/Makefile	2014-10-21 21:29:06.005312990 +0200
-@@ -23,7 +23,7 @@ rec_channel.o rec_channel_rec.o selectmp
- dns_random.o ext/polarssl-1.3.2/library/aes.o ext/polarssl-1.3.2/library/padlock.o dnslabeltext.o \
- lua-pdns.o lua-recursor.o randomhelper.o recpacketcache.o dns.o \
- reczones.o base32.o nsecrecords.o json.o ws-recursor.o ws-api.o \
--version.o responsestats.o webserver.o ext/yahttp/yahttp/reqresp.o \
-+version.o responsestats.o webserver.o ext/yahttp/yahttp/reqresp.o ext/yahttp/yahttp/router.o \
- rec-carbon.o
- 
- REC_CONTROL_OBJECTS=rec_channel.o rec_control.o arguments.o misc.o \
-Index: pdns-recursor/pdns_recursor.cc
-===================================================================
---- pdns-recursor.orig/pdns_recursor.cc	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/pdns_recursor.cc	2014-10-21 21:29:06.005312990 +0200
-@@ -2099,6 +2099,7 @@ int main(int argc, char **argv)
-     ::arg().set("experimental-webserver-port", "Port of webserver to listen on") = "8082";
-     ::arg().set("experimental-webserver-password", "Password required for accessing the webserver") = "";
-     ::arg().set("experimental-api-config-dir", "Directory where REST API stores config and zones") = "";
-+    ::arg().set("experimental-api-key", "REST API Static authentication key (required for API use)") = "";
-     ::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats")="";
-     ::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server")="";
-     ::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates")="30";
-Index: pdns-recursor/webserver.cc
-===================================================================
---- pdns-recursor.orig/webserver.cc	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/webserver.cc	2014-10-21 21:29:06.005312990 +0200
-@@ -20,6 +20,7 @@
-     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
- #include "utility.hh"
-+#include "arguments.hh"
- #include "webserver.hh"
- #include "misc.hh"
- #include <vector>
-@@ -28,6 +29,7 @@
- #include "dns.hh"
- #include "base64.hh"
- #include "json.hh"
-+#include <yahttp/router.hpp>
- 
- struct connectionThreadData {
-   WebServer* webServer;
-@@ -36,11 +38,47 @@ struct connectionThreadData {
- 
- void HttpRequest::json(rapidjson::Document& document)
- {
-+  if(this->body.empty()) {
-+    L<<Logger::Debug<<"HTTP: JSON document expected in request body, but body was empty" << endl;
-+    throw HttpBadRequestException();
-+  }
-   if(document.Parse<0>(this->body.c_str()).HasParseError()) {
-+    L<<Logger::Debug<<"HTTP: parsing of JSON document failed" << endl;
-     throw HttpBadRequestException();
-   }
- }
- 
-+bool HttpRequest::compareAuthorization(const string &expected_password)
-+{
-+  // validate password
-+  YaHTTP::strstr_map_t::iterator header = headers.find("authorization");
-+  bool auth_ok = false;
-+  if (header != headers.end() && toLower(header->second).find("basic ") == 0) {
-+    string cookie = header->second.substr(6);
-+
-+    string plain;
-+    B64Decode(cookie, plain);
-+
-+    vector<string> cparts;
-+    stringtok(cparts, plain, ":");
-+
-+    // this gets rid of terminating zeros
-+    auth_ok = (cparts.size()==2 && (0==strcmp(cparts[1].c_str(), expected_password.c_str())));
-+  }
-+  return auth_ok;
-+}
-+
-+bool HttpRequest::compareHeader(const string &header_name, const string &expected_value)
-+{
-+  YaHTTP::strstr_map_t::iterator header = headers.find(header_name);
-+  if (header == headers.end())
-+    return false;
-+
-+  // this gets rid of terminating zeros
-+  return (0==strcmp(header->second.c_str(), expected_value.c_str()));
-+}
-+
-+
- void HttpResponse::setBody(rapidjson::Document& document)
- {
-   this->body = makeStringFromDocument(document);
-@@ -51,63 +89,44 @@ int WebServer::B64Decode(const std::stri
-   return ::B64Decode(strInput, strOutput);
- }
- 
--// url is supposed to start with a slash.
--// url can contain variable names, marked as <variable>; such variables
--// are parsed out during routing and are put into the "pathArgs" map.
--// route() makes no assumptions about the contents of variables except
--// that the following URL segment can't be part of the variable.
--//
--// Note: ORDER of registration MATTERS:
--// URLs that do a more specific match should come FIRST.
--//
--// Examples:
--//   registerHandler("/foo/<bar>/<baz>", &foobarbaz);
--//   registerHandler("/foo/<bar>", &foobar);
--//   registerHandler("/foo", &foo);
--//   registerHandler("/", &index);
--void WebServer::registerHandler(const string& url, HandlerFunction handler)
-+static void bareHandlerWrapper(WebServer::HandlerFunction handler, YaHTTP::Request* req, YaHTTP::Response* resp)
- {
--  std::size_t pos = 0, lastpos = 0;
--
--  HandlerRegistration reg;
--  while ((pos = url.find('<', lastpos)) != std::string::npos) {
--    std::string part = url.substr(lastpos, pos-lastpos);
--    lastpos = pos;
--    pos = url.find('>', pos);
--
--    if (pos == std::string::npos) {
--      throw std::logic_error("invalid url given");
--    }
-+  // wrapper to convert from YaHTTP::* to our subclasses
-+  handler(static_cast<HttpRequest*>(req), static_cast<HttpResponse*>(resp));
-+}
- 
--    std::string paramName = url.substr(lastpos+1, pos-lastpos-1);
--    lastpos = pos+1;
-+void WebServer::registerBareHandler(const string& url, HandlerFunction handler)
-+{
-+  YaHTTP::THandlerFunction f = boost::bind(&bareHandlerWrapper, handler, _1, _2);
-+  YaHTTP::Router::Any(url, f);
-+}
- 
--    reg.urlParts.push_back(part);
--    reg.paramNames.push_back(paramName);
--  }
--  std::string remainder = url.substr(lastpos);
--  if (!remainder.empty()) {
--    reg.urlParts.push_back(remainder);
--    reg.paramNames.push_back("");
-+static void apiWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp) {
-+  const string& api_key = ::arg()["experimental-api-key"];
-+  if (api_key.empty()) {
-+    L<<Logger::Debug<<"HTTP API Request \"" << req->url.path << "\": Authentication failed, API Key missing in config" << endl;
-+    throw HttpUnauthorizedException();
-+  }
-+  bool auth_ok = req->compareHeader("x-api-key", api_key);
-+  if (!auth_ok) {
-+    L<<Logger::Debug<<"HTTP Request \"" << req->url.path << "\": Authentication by API Key failed" << endl;
-+    throw HttpUnauthorizedException();
-   }
--  reg.handler = handler;
--  d_handlers.push_back(reg);
--}
- 
--static void apiWrapper(boost::function<void(HttpRequest*,HttpResponse*)> handler, HttpRequest* req, HttpResponse* resp) {
-   resp->headers["Access-Control-Allow-Origin"] = "*";
-   resp->headers["Content-Type"] = "application/json";
- 
-   string callback;
- 
--  if(req->parameters.count("callback")) {
--    callback=req->parameters["callback"];
--    req->parameters.erase("callback");
-+  if(req->getvars.count("callback")) {
-+    callback=req->getvars["callback"];
-+    req->getvars.erase("callback");
-   }
- 
--  req->parameters.erase("_"); // jQuery cache buster
-+  req->getvars.erase("_"); // jQuery cache buster
- 
-   try {
-+    resp->status = 200;
-     handler(req, resp);
-   } catch (ApiException &e) {
-     resp->body = returnJsonError(e.what());
-@@ -131,47 +150,25 @@ static void apiWrapper(boost::function<v
- 
- void WebServer::registerApiHandler(const string& url, HandlerFunction handler) {
-   HandlerFunction f = boost::bind(&apiWrapper, handler, _1, _2);
--  registerHandler(url, f);
-+  registerBareHandler(url, f);
- }
- 
--bool WebServer::route(const std::string& url, std::map<std::string, std::string>& pathArgs, HandlerFunction** handler)
--{
--  for (std::list<HandlerRegistration>::iterator reg=d_handlers.begin(); reg != d_handlers.end(); ++reg) {
--    bool matches = true;
--    size_t lastpos = 0, pos = 0;
--    string lastParam;
--    pathArgs.clear();
--    for (std::list<string>::iterator urlPart = reg->urlParts.begin(), param = reg->paramNames.begin();
--         urlPart != reg->urlParts.end() && param != reg->paramNames.end();
--         urlPart++, param++) {
--      if (!urlPart->empty()) {
--        pos = url.find(*urlPart, lastpos);
--        if (pos == std::string::npos) {
--          matches = false;
--          break;
--        }
--        if (!lastParam.empty()) {
--          // store
--          pathArgs[lastParam] = url.substr(lastpos, pos-lastpos);
--        }
--        lastpos = pos + urlPart->size();
--        lastParam = *param;
--      }
--    }
--    if (matches) {
--      if (!lastParam.empty()) {
--        // store trailing parameter
--        pathArgs[lastParam] = url.substr(lastpos, pos-lastpos);
--      } else if (lastpos != url.size()) {
--        matches = false;
--        continue;
--      }
--
--      *handler = &reg->handler;
--      return true;
-+static void webWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp) {
-+  const string& web_password = arg()["webserver-password"];
-+  if (!web_password.empty()) {
-+    bool auth_ok = req->compareAuthorization(web_password);
-+    if (!auth_ok) {
-+      L<<Logger::Debug<<"HTTP Request \"" << req->url.path << "\": Web Authentication failed" << endl;
-+      throw HttpUnauthorizedException();
-     }
-   }
--  return false;
-+
-+  handler(req, resp);
-+}
-+
-+void WebServer::registerWebHandler(const string& url, HandlerFunction handler) {
-+  HandlerFunction f = boost::bind(&webWrapper, handler, _1, _2);
-+  registerBareHandler(url, f);
- }
- 
- static void *WebServerConnectionThreadStart(void *p) {
-@@ -187,13 +184,14 @@ static void *WebServerConnectionThreadSt
- 
- HttpResponse WebServer::handleRequest(HttpRequest req)
- {
--  HttpResponse resp(req);
-+  HttpResponse resp;
- 
-   // set default headers
-   resp.headers["Content-Type"] = "text/html; charset=utf-8";
- 
-   try {
-     if (!req.complete) {
-+      L<<Logger::Debug<<"HTTP: Incomplete request" << endl;
-       throw HttpBadRequestException();
-     }
- 
-@@ -210,38 +208,19 @@ HttpResponse WebServer::handleRequest(Ht
-       }
-     }
- 
--    if (!d_password.empty()) {
--      // validate password
--      header = req.headers.find("authorization");
--      bool auth_ok = false;
--      if (header != req.headers.end() && toLower(header->second).find("basic ") == 0) {
--        string cookie = header->second.substr(6);
--
--        string plain;
--        B64Decode(cookie, plain);
--
--        vector<string> cparts;
--        stringtok(cparts, plain, ":");
--
--        // this gets rid of terminating zeros
--        auth_ok = (cparts.size()==2 && (0==strcmp(cparts[1].c_str(), d_password.c_str())));
--      }
--      if (!auth_ok) {
--        L<<Logger::Debug<<"HTTP Request \"" << req.url.path << "\": Authentication failed" << endl;
--        throw HttpUnauthorizedException();
--      }
--    }
--
--    HandlerFunction *handler;
--    if (!route(req.url.path, req.path_parameters, &handler)) {
-+    YaHTTP::THandlerFunction handler;
-+    if (!YaHTTP::Router::Route(&req, handler)) {
-       L<<Logger::Debug<<"HTTP: No route found for \"" << req.url.path << "\"" << endl;
-       throw HttpNotFoundException();
-     }
- 
-     try {
--      (*handler)(&req, &resp);
-+      handler(&req, &resp);
-       L<<Logger::Debug<<"HTTP: Result for \"" << req.url.path << "\": " << resp.status << ", body length: " << resp.body.size() << endl;
-     }
-+    catch(HttpException) {
-+      throw;
-+    }
-     catch(PDNSException &e) {
-       L<<Logger::Error<<"HTTP ISE for \""<< req.url.path << "\": Exception: " << e.reason << endl;
-       throw HttpInternalServerErrorException();
-@@ -281,7 +260,8 @@ HttpResponse WebServer::handleRequest(Ht
- void WebServer::serveConnection(Socket *client)
- try {
-   HttpRequest req;
--  YaHTTP::AsyncRequestLoader yarl(&req);
-+  YaHTTP::AsyncRequestLoader yarl;
-+  yarl.initialize(&req);
-   int timeout = 5;
-   client->setNonBlocking();
- 
-@@ -298,6 +278,7 @@ try {
-         break;
-       }
-     }
-+    yarl.finalize();
-   } catch (YaHTTP::ParseError &e) {
-     // request stays incomplete
-   }
-@@ -319,11 +300,10 @@ catch(...) {
-   L<<Logger::Error<<"HTTP: Unknown exception"<<endl;
- }
- 
--WebServer::WebServer(const string &listenaddress, int port, const string &password) : d_server(NULL)
-+WebServer::WebServer(const string &listenaddress, int port) : d_server(NULL)
- {
-   d_listenaddress=listenaddress;
-   d_port=port;
--  d_password=password;
- }
- 
- void WebServer::bind()
-Index: pdns-recursor/webserver.hh
-===================================================================
---- pdns-recursor.orig/webserver.hh	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/webserver.hh	2014-10-21 21:29:06.005312990 +0200
-@@ -32,21 +32,25 @@
- #include "namespaces.hh"
- #include "sstuff.hh"
- 
-+class WebServer;
-+
- class HttpRequest : public YaHTTP::Request {
- public:
-   HttpRequest() : YaHTTP::Request(), accept_json(false), accept_html(false), complete(false) { };
- 
--  map<string,string> path_parameters;
-   bool accept_json;
-   bool accept_html;
-   bool complete;
-   void json(rapidjson::Document& document);
-+
-+  // checks password _only_.
-+  bool compareAuthorization(const string &expected_password);
-+  bool compareHeader(const string &header_name, const string &expected_value);
- };
- 
- class HttpResponse: public YaHTTP::Response {
- public:
-   HttpResponse() : YaHTTP::Response() { };
--  HttpResponse(const YaHTTP::Request &req) : YaHTTP::Response(req) { };
-   HttpResponse(const YaHTTP::Response &resp) : YaHTTP::Response(resp) { };
- 
-   void setBody(rapidjson::Document& document);
-@@ -127,7 +131,7 @@ protected:
- class WebServer : public boost::noncopyable
- {
- public:
--  WebServer(const string &listenaddress, int port, const string &password="");
-+  WebServer(const string &listenaddress, int port);
-   void bind();
-   void go();
- 
-@@ -135,19 +139,13 @@ public:
-   HttpResponse handleRequest(HttpRequest request);
- 
-   typedef boost::function<void(HttpRequest* req, HttpResponse* resp)> HandlerFunction;
--  struct HandlerRegistration {
--    std::list<string> urlParts;
--    std::list<string> paramNames;
--    HandlerFunction handler;
--  };
--
--  void registerHandler(const string& url, HandlerFunction handler);
-   void registerApiHandler(const string& url, HandlerFunction handler);
-+  void registerWebHandler(const string& url, HandlerFunction handler);
- 
- protected:
-   static char B64Decode1(char cInChar);
-   static int B64Decode(const std::string& strInput, std::string& strOutput);
--  bool route(const std::string& url, std::map<std::string, std::string>& urlArgs, HandlerFunction** handler);
-+  void registerBareHandler(const string& url, HandlerFunction handler);
- 
-   virtual Server* createServer() {
-     return new Server(d_listenaddress, d_port);
-@@ -155,7 +153,6 @@ protected:
- 
-   string d_listenaddress;
-   int d_port;
--  std::list<HandlerRegistration> d_handlers;
-   string d_password;
-   Server* d_server;
- };
-Index: pdns-recursor/ws-api.cc
-===================================================================
---- pdns-recursor.orig/ws-api.cc	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ws-api.cc	2014-10-21 21:29:06.005312990 +0200
-@@ -188,7 +188,7 @@ void apiServerSearchLog(HttpRequest* req
-     throw HttpMethodNotAllowedException();
- 
-   string prefix = " " + s_programname + "[";
--  resp->body = logGrep(req->parameters["q"], ::arg()["experimental-logfile"], prefix);
-+  resp->body = logGrep(req->getvars["q"], ::arg()["experimental-logfile"], prefix);
- }
- 
- void apiServerStatistics(HttpRequest* req, HttpResponse* resp) {
-Index: pdns-recursor/ws-recursor.cc
-===================================================================
---- pdns-recursor.orig/ws-recursor.cc	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ws-recursor.cc	2014-10-21 21:29:06.005312990 +0200
-@@ -331,7 +331,7 @@ static void apiServerZones(HttpRequest*
- 
- static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp)
- {
--  string zonename = apiZoneIdToName(req->path_parameters["id"]);
-+  string zonename = apiZoneIdToName(req->parameters["id"]);
-   zonename += ".";
- 
-   SyncRes::domainmap_t::const_iterator iter = t_sstorage->domainmap->find(zonename);
-@@ -367,7 +367,7 @@ static void apiServerSearchData(HttpRequ
-   if(req->method != "GET")
-     throw HttpMethodNotAllowedException();
- 
--  string q = req->parameters["q"];
-+  string q = req->getvars["q"];
-   if (q.empty())
-     throw ApiException("Query q can't be blank");
- 
-@@ -420,7 +420,7 @@ RecursorWebServer::RecursorWebServer(FDM
- {
-   RecursorControlParser rcp; // inits
- 
--  d_ws = new AsyncWebServer(fdm, arg()["experimental-webserver-address"], arg().asNum("experimental-webserver-port"), arg()["experimental-webserver-password"]);
-+  d_ws = new AsyncWebServer(fdm, arg()["experimental-webserver-address"], arg().asNum("experimental-webserver-port"));
-   d_ws->bind();
- 
-   // legacy dispatch
-@@ -442,9 +442,9 @@ void RecursorWebServer::jsonstat(HttpReq
- {
-   string command;
- 
--  if(req->parameters.count("command")) {
--    command = req->parameters["command"];
--    req->parameters.erase("command");
-+  if(req->getvars.count("command")) {
-+    command = req->getvars["command"];
-+    req->getvars.erase("command");
-   }
- 
-   map<string, string> stats; 
-@@ -476,7 +476,7 @@ void RecursorWebServer::jsonstat(HttpReq
-     return;
-   }
-   else if(command == "zone") {
--    string arg_zone = req->parameters["zone"];
-+    string arg_zone = req->getvars["zone"];
-     SyncRes::domainmap_t::const_iterator ret = t_sstorage->domainmap->find(arg_zone);
-     if (ret != t_sstorage->domainmap->end()) {
-       Document doc;
-@@ -525,7 +525,7 @@ void RecursorWebServer::jsonstat(HttpReq
-     }
-   }
-   else if(command == "flush-cache") {
--    string canon=toCanonic("", req->parameters["domain"]);
-+    string canon=toCanonic("", req->getvars["domain"]);
-     int count = broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, canon));
-     count+=broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, canon));
-     stats["number"]=lexical_cast<string>(count);
-@@ -542,7 +542,7 @@ void RecursorWebServer::jsonstat(HttpReq
-   }
-   else if(command == "log-grep") {
-     // legacy parameter name hack
--    req->parameters["q"] = req->parameters["needle"];
-+    req->getvars["q"] = req->getvars["needle"];
-     apiServerSearchLog(req, resp);
-     return;
-   }
-@@ -584,7 +584,8 @@ void AsyncServer::newConnection()
- void AsyncWebServer::serveConnection(Socket *client)
- {
-   HttpRequest req;
--  YaHTTP::AsyncRequestLoader yarl(&req);
-+  YaHTTP::AsyncRequestLoader yarl;
-+  yarl.initialize(&req);
-   client->setNonBlocking();
- 
-   string data;
-@@ -599,6 +600,7 @@ void AsyncWebServer::serveConnection(Soc
-         break;
-       }
-     }
-+    yarl.finalize();
-   } catch (YaHTTP::ParseError &e) {
-     // request stays incomplete
-   }
-Index: pdns-recursor/ws-recursor.hh
-===================================================================
---- pdns-recursor.orig/ws-recursor.hh	2014-10-21 21:29:06.005312990 +0200
-+++ pdns-recursor/ws-recursor.hh	2014-10-21 21:29:06.005312990 +0200
-@@ -45,8 +45,8 @@ private:
- class AsyncWebServer : public WebServer
- {
- public:
--  AsyncWebServer(FDMultiplexer* fdm, const string &listenaddress, int port, const string &password="") :
--    WebServer(listenaddress, port, password), d_fdm(fdm) { };
-+  AsyncWebServer(FDMultiplexer* fdm, const string &listenaddress, int port) :
-+    WebServer(listenaddress, port), d_fdm(fdm) { };
-   void go();
- 
- private:
diff -Nru pdns-recursor-3.6.1/debian/patches/series pdns-recursor-3.6.2/debian/patches/series
--- pdns-recursor-3.6.1/debian/patches/series	2014-10-21 20:52:44.000000000 +0200
+++ pdns-recursor-3.6.2/debian/patches/series	2014-10-30 14:17:30.000000000 +0100
@@ -1 +0,0 @@
-apikey-3-6-2.patch
diff -Nru pdns-recursor-3.6.1/ext/yahttp/.gitignore pdns-recursor-3.6.2/ext/yahttp/.gitignore
--- pdns-recursor-3.6.1/ext/yahttp/.gitignore	2014-09-09 10:56:07.000000000 +0200
+++ pdns-recursor-3.6.2/ext/yahttp/.gitignore	2014-09-16 20:26:07.000000000 +0200
@@ -1,2 +1,4 @@
 yahttp/libyahttp.a
 *.o
+Makefile
+Makefile.in
diff -Nru pdns-recursor-3.6.1/ext/yahttp/LICENSE pdns-recursor-3.6.2/ext/yahttp/LICENSE
--- pdns-recursor-3.6.1/ext/yahttp/LICENSE	2014-09-09 10:56:07.000000000 +0200
+++ pdns-recursor-3.6.2/ext/yahttp/LICENSE	2014-09-16 20:26:07.000000000 +0200
@@ -1,502 +1,21 @@
-GNU LESSER GENERAL PUBLIC LICENSE
-                       Version 2.1, February 1999
+The MIT License (MIT)
 
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
+Copyright (c) 2014 Aki Tuomi
 
-[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.
-
-    {{description}}
-    Copyright (C) {{year}}  {{fullname}}
-
-    This library is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2.1 of the License, or (at your option) any later version.
-
-    This library is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the
-  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
-  {signature of Ty Coon}, 1 April 1990
-  Ty Coon, President of Vice
-
-That's all there is to it!
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff -Nru pdns-recursor-3.6.1/ext/yahttp/Makefile pdns-recursor-3.6.2/ext/yahttp/Makefile
--- pdns-recursor-3.6.1/ext/yahttp/Makefile	2014-09-09 10:56:07.000000000 +0200
+++ pdns-recursor-3.6.2/ext/yahttp/Makefile	1970-01-01 01:00:00.000000000 +0100
@@ -1,22 +0,0 @@
-.SILENT:
-
-all:
-	$(MAKE) -C yahttp all
-
-install:
-
-uninstall:
-
-distclean: clean
-
-clean:
-	$(MAKE) -C yahttp clean
-
-check:
-
-distdir:
-	mkdir -p $(distdir)
-	cp LICENSE Makefile $(distdir)
-	mkdir $(distdir)/yahttp
-	cp yahttp/Makefile yahttp/*.cpp yahttp/*.hpp $(distdir)/yahttp
-
diff -Nru pdns-recursor-3.6.1/ext/yahttp/Makefile.am pdns-recursor-3.6.2/ext/yahttp/Makefile.am
--- pdns-recursor-3.6.1/ext/yahttp/Makefile.am	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor-3.6.2/ext/yahttp/Makefile.am	2014-10-30 12:25:25.000000000 +0100
@@ -0,0 +1,3 @@
+SUBDIRS=yahttp
+
+EXTRA_DIST=LICENSE README.md
diff -Nru pdns-recursor-3.6.1/ext/yahttp/README.md pdns-recursor-3.6.2/ext/yahttp/README.md
--- pdns-recursor-3.6.1/ext/yahttp/README.md	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor-3.6.2/ext/yahttp/README.md	2014-09-16 20:26:07.000000000 +0200
@@ -0,0 +1,62 @@
+Yet Another HTTP Library
+========================
+
+YaHTTP aims to be a pure http request/response parser that has no IO ties. It is intended to be used on small-footprint applications and other utilities that want to use HTTP over something else than network IO.
+
+[![Build Status](https://travis-ci.org/cmouse/yahttp.svg?branch=master)](https://travis-ci.org/cmouse/yahttp)
+[![Coverity Scan Build Status](https://scan.coverity.com/projects/2161/badge.svg)](https://scan.coverity.com/projects/2161)
+
+WARNINGS
+--------
+If you are upgrading from version before May 02, 2014 - *PLEASE BE SURE TO CHECK THAT EVERYTHING WORKS AS EXPECTED*. There has been complete overhaul of the library and many things have changed. 
+
+Integration guide
+-----------------
+
+Here are some instructions on how to integrate YaHTTP into your project. 
+
+With automake and libtool
+-------------------------
+
+If you don't need router or any of the C++11 features, you can just create empty yahttp-config.h, or symlink it to your project's config.h for the yahttp.hpp to include. Then just put this stuff into it's own folder and create Makefile.am with following contents (you can change the compilation flags):
+
+```
+noinst_LTLIBRARIES=libyahttp.la
+libyahttp_la_CXXFLAGS=$(RELRO_CFLAGS) $(PIE_CFLAGS) -D__STRICT_ANSI__
+libyahttp_la_SOURCES=cookie.hpp exception.hpp reqresp.cpp reqresp.hpp router.cpp router.hpp url.hpp utility.hpp yahttp.hpp
+```
+
+You can define RELRO and PIE to match your project. 
+
+To compile your project use -Lpath/to/yahttp -lyahttp
+
+If you need router, additionally check for boost or C++11 and replace yahttp-config.h to config.h in yahttp.hpp or add relevant options to your compiler CXXFLAGS. See below for the flags.
+
+Without automake
+----------------
+
+Create simple Makefile with contents for C++11:
+
+```
+OBJECTS=reqresp.o router.o
+CXX=gcc
+CXXFLAGS=-W -Wall -DHAVE_CXX11 -std=c++11 
+```
+
+Or create simple Makefile with contents for boost:
+
+```
+OBJECTS=reqresp.o router.o
+CXX=gcc
+CXXFLAGS=-W -Wall -DHAVE_BOOST 
+```
+
+Or if you don't need either one, just:
+
+```
+OBJECTS=reqresp.o 
+CXX=gcc
+CXXFLAGS=-W -Wall
+```
+
+YaHTTP include files can be placed where the rest of your includes are. Then just add your own code there and it should work just fine. 
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/cookie.hpp pdns-recursor-3.6.2/ext/yahttp/yahttp/cookie.hpp
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/cookie.hpp	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/cookie.hpp	2014-09-16 20:26:07.000000000 +0200
@@ -0,0 +1,135 @@
+namespace YaHTTP {
+  /*! Implements a single cookie */
+  class Cookie {
+  public:
+     Cookie() {
+       secure = false;
+       httponly = false;
+       name = value = "";
+     }; //!< Set the cookie to empty value
+
+     Cookie(const Cookie &rhs) {
+       domain = rhs.domain;
+       path = rhs.path;
+       secure = rhs.secure;
+       httponly = rhs.httponly;
+       name = rhs.name;
+       value = rhs.value;
+     }; //<! Copy cookie values
+
+     DateTime expires; /*!< Expiration date */
+     std::string domain; /*!< Domain where cookie is valid */
+     std::string path; /*!< Path where the cookie is valid */
+     bool httponly; /*!< Whether the cookie is for server use only */
+     bool secure; /*!< Whether the cookie is for HTTPS only */
+ 
+     std::string name; /*!< Cookie name */
+     std::string value; /*!< Cookie value */
+
+     std::string str() const {
+       std::ostringstream oss;
+       oss << YaHTTP::Utility::encodeURL(name) << "=" << YaHTTP::Utility::encodeURL(value);
+       if (expires.isSet) 
+         oss << "; expires=" << expires.cookie_str();
+       if (domain.size()>0)
+         oss << "; domain=" << domain;
+       if (path.size()>0)
+         oss << "; path=" << path;
+       if (secure)
+         oss << "; secure";
+       if (httponly)
+         oss << "; httpOnly";
+       return oss.str();
+     }; //!< Stringify the cookie
+  };
+
+  /*! Implements a Cookie jar for storing multiple cookies */
+  class CookieJar {
+    public:
+    std::map<std::string, Cookie> cookies;  //<! cookie container
+  
+    CookieJar() {}; //<! constructs empty cookie jar
+    CookieJar(const CookieJar & rhs) {
+      this->cookies = rhs.cookies;
+    } //<! copy cookies from another cookie jar
+  
+    void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value) {
+      size_t pos;
+      pos = keyvalue.find("=");
+      if (pos == std::string::npos) throw "Not a Key-Value pair (cookie)";
+      key = std::string(keyvalue.begin(), keyvalue.begin()+pos);
+      value = std::string(keyvalue.begin()+pos+1, keyvalue.end());
+    } //<! key value pair parser
+  
+    void parseCookieHeader(const std::string &cookiestr) {
+      std::list<Cookie> cookies;
+      int cstate = 0; //cookiestate
+      size_t pos,npos;
+      pos = 0;
+      cstate = 0;
+      while(pos < cookiestr.size()) {
+        if (cookiestr.compare(pos, 7, "expires") ==0 ||
+            cookiestr.compare(pos, 6, "domain")  ==0 ||
+            cookiestr.compare(pos, 4, "path")    ==0) {
+          cstate = 1;
+          // get the date
+          std::string key, value, s;
+          npos = cookiestr.find("; ", pos);
+          if (npos == std::string::npos) {
+            // last value
+            s = std::string(cookiestr.begin() + pos + 1, cookiestr.end());
+            pos = cookiestr.size();
+          } else {
+            s = std::string(cookiestr.begin() + pos + 1, cookiestr.begin() + npos - 1);
+            pos = npos+2;
+          }
+          keyValuePair(s, key, value);
+          if (s == "expires") {
+            DateTime dt;
+            dt.parseCookie(value);
+            for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
+              i->expires = dt;
+          } else if (s == "domain") {
+            for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
+              i->domain = value;
+          } else if (s == "path") {
+            for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
+              i->path = value;
+          }
+        } else if (cookiestr.compare(pos, 8, "httpOnly")==0) {
+          cstate = 1;
+          for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
+            i->httponly = true;
+        } else if (cookiestr.compare(pos, 6, "secure")  ==0) {
+          cstate = 1;
+          for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
+            i->secure = true;
+        } else if (cstate == 0) { // expect cookie
+          Cookie c;
+          std::string s;
+          npos = cookiestr.find("; ", pos);
+          if (npos == std::string::npos) {
+            // last value
+            s = std::string(cookiestr.begin() + pos, cookiestr.end());
+            pos = cookiestr.size();
+          } else {
+            s = std::string(cookiestr.begin() + pos, cookiestr.begin() + npos);
+            pos = npos+2;
+          }
+          keyValuePair(s, c.name, c.value);
+          c.name = YaHTTP::Utility::decodeURL(c.name);
+          c.value = YaHTTP::Utility::decodeURL(c.value);
+          cookies.push_back(c);
+        } else if (cstate == 1) {
+          // ignore crap
+          break;
+        }
+      }
+  
+      // store cookies
+      for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++) {
+        this->cookies[i->name] = *i;
+      }
+    }; //<! Parse multiple cookies from header 
+  };
+};
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/exception.hpp pdns-recursor-3.6.2/ext/yahttp/yahttp/exception.hpp
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/exception.hpp	2014-09-09 10:56:07.000000000 +0200
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/exception.hpp	2014-09-16 20:26:07.000000000 +0200
@@ -4,17 +4,24 @@
 #include <exception>
 
 namespace YaHTTP {
-  class ParseError: public std::exception {
+  /*! Generic error class */
+  class Error: public std::exception {
   public:
-    ParseError() {};
-    ParseError(const std::string& reason): reason(reason) {};
-    virtual ~ParseError() throw() {}; 
+    Error() {};
+    Error(const std::string& reason): reason(reason) {};
+    virtual ~Error() throw() {};
 
     virtual const char* what() const throw()
     {
       return reason.c_str();
     }
-    const std::string reason;
+    const std::string reason; //<! Cause of the error
+  };
+  /*! Parse error class */
+  class ParseError: public YaHTTP::Error {
+  public:
+    ParseError() {};
+    ParseError(const std::string& reason): Error(reason) {};
   };
 };
 
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/Makefile pdns-recursor-3.6.2/ext/yahttp/yahttp/Makefile
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/Makefile	2014-09-09 10:56:07.000000000 +0200
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/Makefile	1970-01-01 01:00:00.000000000 +0100
@@ -1,14 +0,0 @@
-
-all: libyahttp.a
-
-
-clean:
-	-rm -f *.o *.a
-
-
-reqresp.o: *.cpp *.hpp
-
-
-libyahttp.a: reqresp.o
-	echo AR $@
-	$(AR) rcs $@ $<
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/Makefile.am pdns-recursor-3.6.2/ext/yahttp/yahttp/Makefile.am
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/Makefile.am	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/Makefile.am	2014-10-30 12:25:25.000000000 +0100
@@ -0,0 +1,3 @@
+noinst_LTLIBRARIES=libyahttp.la
+
+libyahttp_la_SOURCES=cookie.hpp exception.hpp reqresp.cpp reqresp.hpp router.cpp router.hpp url.hpp utility.hpp yahttp-config.h yahttp.hpp
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/reqresp.cpp pdns-recursor-3.6.2/ext/yahttp/yahttp/reqresp.cpp
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/reqresp.cpp	2014-09-09 10:56:07.000000000 +0200
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/reqresp.cpp	2014-09-16 20:26:07.000000000 +0200
@@ -1,306 +1,229 @@
-#include "reqresp.hpp"
+#include "yahttp.hpp"
 
 namespace YaHTTP {
-
-  void Request::build(const std::string &method, const std::string &url, const std::string &params) {
-    this->method = method;
-    std::transform(this->method.begin(), this->method.end(), this->method.begin(), ::toupper);
-    this->url.parse(url);
-    this->headers["host"] = this->url.host;
-    this->headers["connection"] = "close";
-    this->headers["user-agent"] = "yahttp 0.1";
-    this->headers["accept"] = "*/*";
-    if (params.empty() == false) {
-      this->headers["content-type"] = "application/x-www-form-urlencoded; charset=utf-8";
-      this->headers["content-length"] = params.size();
-      this->body = params;
-    } else {
-      this->body = "";
-    }
-  };
-
-  Request::Request() {};
-  Request::Request(const Response &resp) {
-    method = resp.method;
-    url = resp.url;
-    cookies = resp.cookies;
-  };
-  Request::Request(const Request &req) {
-    method = req.method;
-    url = req.url;
-    parameters = req.parameters;
-    headers = req.headers;
-    cookies = req.cookies;
-    body = req.body;
-  };
-  Request::~Request() {};
-
-  Response::Response() : status(0) {};
-  Response::Response(const Request &req) {
-    headers["connection"] = "close";
-    method = req.method;
-    url = req.url;
-    cookies = req.cookies;
-    status = 200;
-  };
-  Response::Response(const Response &resp) {
-    method = resp.method;
-    url = resp.url;
-    parameters = resp.parameters;
-    headers = resp.headers;
-    cookies = resp.cookies;
-    body = resp.body;
-    status = resp.status;
-    statusText = resp.statusText;
-  };
-  Response::~Response() {};
-
-  void Response::load(std::istream &is) {
-    AsyncResponseLoader arl(this);
-    while(is.good()) {
-      char buf[1024];
-      is.read(buf, 1024);
-      if (is.gcount()) { // did we actually read anything 
-        is.clear();
-        if (arl.feed(std::string(buf, is.gcount())) == true) return; // completed
-      }
-    }
-    // FIXME: parse cookies
-  };
-
-  void Response::write(std::ostream &os) const { 
-    os << "HTTP/1.1 " << status << " ";
-    if (statusText.empty()) 
-      os << Utility::status2text(status);
-    else
-      os << statusText;
-    os << "\r\n";
-
-    // write headers
-    strstr_map_t::const_iterator iter = headers.begin();
-    while(iter != headers.end()) {
-      os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
-      iter++;
-    }
-    os << "\r\n";
-    os << body;
-  };
- 
-  void Request::load(std::istream &is) {
-    AsyncRequestLoader arl(this);
-    while(is.good()) {
-      char buf[1024];
-      is.read(buf, 1024);
-      if (is.gcount()) { // did we actually read anything
-        is.clear();
-        if (arl.feed(std::string(buf, is.gcount())) == true) return; // completed
-      }
-    }
-  };
-
-  void Request::write(std::ostream &os) const {
-    os << method << " " << url.path << " HTTP/1.1" << "\r\n";
-    strstr_map_t::const_iterator iter = headers.begin();
-    while(iter != headers.end()) {
-      os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
-      iter++;
-    }
-    os << "\r\n";
-    if (body.size()>0) {
-      os << body;
-    }
-  };
-
-  std::ostream& operator<<(std::ostream& os, const Response &resp) {
-    resp.write(os);
-    return os;
-  };
-
-  std::istream& operator>>(std::istream& is, Response &resp) {
-    resp.load(is);
-    return is;
-  };
-
-  std::ostream& operator<<(std::ostream& os, const Request &req) {
-    req.write(os);
-    return os;
-  };
-
-  std::istream& operator>>(std::istream& is, Request &req) {
-    req.load(is);
-    return is;
-  };
-
-  bool AsyncRequestLoader::feed(const std::string &somedata) {
-    size_t pos;
-
+  template <class T>
+  int AsyncLoader<T>::feed(const std::string& somedata) {
     buffer.append(somedata);
     while(state < 2) {
-      // need to find newline in buffer
-      if ((pos = buffer.find("\r\n")) == std::string::npos) return false;
-      std::string line(buffer.begin(), buffer.begin()+pos); // exclude CRLF
-      buffer.erase(buffer.begin(), buffer.begin()+pos+2); // remove line from buffer including CRLF
+      int cr=0;
+      // need to find CRLF in buffer
+      if ((pos = buffer.find_first_of("\n")) == std::string::npos) return false;
+      if (buffer[pos-1]=='\r')
+        cr=1;
+      std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
+      buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
+
       if (state == 0) { // startup line
-        std::string ver;
-        std::string tmpurl;
-        std::istringstream iss(line);
-        iss >> request->method >> tmpurl >> ver;
-        if (ver.find("HTTP/1.") != 0)
-          throw ParseError("Not a HTTP 1.x request");
-        // uppercase the request method
-        std::transform(request->method.begin(), request->method.end(), request->method.begin(), ::toupper);
-        request->url.parse(tmpurl);
-        request->parameters = Utility::parseUrlParameters(request->url.parameters);
-        state = 1;
+        if (target->kind == YAHTTP_TYPE_REQUEST) {
+          std::string ver;
+          std::string tmpurl;
+          std::istringstream iss(line);
+          iss >> target->method >> tmpurl >> ver;
+          if (ver.find("HTTP/1.") != 0)
+            throw ParseError("Not a HTTP 1.x request");
+          // uppercase the target method
+          std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
+          target->url.parse(tmpurl);
+          target->getvars = Utility::parseUrlParameters(target->url.parameters);
+          state = 1;
+        } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
+          std::string ver;
+          std::istringstream iss(line);
+          iss >> ver >> target->status >> target->statusText;
+          if (ver.find("HTTP/1.") != 0)
+            throw ParseError("Not a HTTP 1.x response");
+          state = 1;
+        }
       } else if (state == 1) {
         std::string key,value;
         size_t pos;
         if (line.empty()) {
-          chunked = (request->headers.find("transfer-encoding") != request->headers.end() && request->headers["transfer-encoding"] == "chunked");
-          // host header is optional
-          if (request->headers.find("host") != request->headers.end())
-            request->url.host = request->headers["host"];
-               
+          chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
           state = 2;
           break;
         }
         // split headers
         if ((pos = line.find_first_of(": ")) == std::string::npos)
-          throw ParseError("Malformed line");
+          throw ParseError("Malformed header line");
         key = line.substr(0, pos);
         value = line.substr(pos+2);
+        Utility::trimRight(value);
         std::transform(key.begin(), key.end(), key.begin(), ::tolower);
-        request->headers[key] = value;
+        // is it already defined
+
+        if ((key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) ||
+            (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST)) {
+          target->jar.parseCookieHeader(value);
+        } else {
+          if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
+            // maybe it contains port? 
+            if ((pos = value.find(":")) == std::string::npos) {
+              target->url.host = value;
+            } else {
+              target->url.host = value.substr(0, pos);
+              target->url.port = ::atoi(value.substr(pos).c_str());
+            }
+          }
+          if (target->headers.find(key) != target->headers.end()) {
+            target->headers[key] = target->headers[key] + ";" + value;
+          } else {
+            target->headers[key] = value;
+          }
+        }
       }
     }
 
-    // skip body for GET
-    if (request->method == "GET")
-      return true;
-       
-    // do we have content-length? 
+    minbody = 0;
+    // check for expected body size
+    if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size;
+    else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size; 
+    else maxbody = 0;
+   
     if (!chunked) {
-      if (request->headers.find("content-length") != request->headers.end()) {
-        std::istringstream maxbodyS(request->headers["content-length"]);
-        maxbodyS >> maxbody;
+      if (target->headers.find("content-length") != target->headers.end()) {
+        std::istringstream maxbodyS(target->headers["content-length"]);
+        maxbodyS >> minbody;
+        maxbody = minbody;
       }
-      if (maxbody < 1) return true; // guess there isn't anything left.
-      if (maxbody > YAHTTP_MAX_REQUEST_SIZE) 
-        throw ParseError("Request size exceeded");
+      if (minbody < 1) return true; // guess there isn't anything left.
+      if (target->kind == YAHTTP_TYPE_REQUEST && static_cast<ssize_t>(minbody) > target->max_request_size) throw ParseError("Max request body size exceeded");
+      else if (target->kind == YAHTTP_TYPE_RESPONSE && static_cast<ssize_t>(minbody) > target->max_response_size) throw ParseError("Max response body size exceeded");
     }
 
-    if (buffer.size() == 0) return false;
+    if (maxbody == 0) hasBody = false;
+    else hasBody = true;
 
-    while(buffer.size() > 0 && bodybuf.str().size() < static_cast<size_t>(maxbody)) {
-      char buf[1024] = {0};
+    if (buffer.size() == 0) return ready();
 
+    while(buffer.size() > 0) {
       if (chunked) {
         if (chunk_size == 0) {
+          char buf[100];
           // read chunk length
           if ((pos = buffer.find('\n')) == std::string::npos) return false;
-          if (pos > 1023)
+          if (pos > 99)
             throw ParseError("Impossible chunk_size");
           buffer.copy(buf, pos);
           buf[pos]=0; // just in case...
           buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
           sscanf(buf, "%x", &chunk_size);
-          if (!chunk_size) break; // last chunk
+          if (!chunk_size) { state = 3; break; } // last chunk
         } else {
+          int crlf=1;
           if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
-          if (buffer.at(chunk_size) != '\n') return false; // there should be newline.
-          buffer.copy(buf, chunk_size);
-          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+1);
-          bodybuf << buf;
+          if (buffer.at(chunk_size) == '\r') {
+            if (buffer.size() < static_cast<size_t>(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return
+            crlf=2;
+          } else if (buffer.at(chunk_size) != '\n') return false; 
+          std::string tmp = buffer.substr(0, chunk_size);
+          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
+          bodybuf << tmp;
           chunk_size = 0;
-          if (buffer.size() == 0) return false; // just in case
+          if (buffer.size() == 0) break; // just in case
         }
       } else {
-        bodybuf << buffer;
+        if (bodybuf.str().length() + buffer.length() > maxbody) 
+          bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
+        else 
+          bodybuf << buffer;
         buffer = "";
       }
     }
 
     if (chunk_size!=0) return false; // need more data
 
-    if (!chunked && bodybuf.str().size() < maxbody) {
-      return false; // need more data
-    }
-
-    bodybuf.flush();
-    request->body = bodybuf.str();
-    bodybuf.str("");
-    return true;
+    return ready();
   };
-
-  bool AsyncResponseLoader::feed(const std::string &somedata) {
-    size_t pos;
-    buffer.append(somedata);
-    while(state < 2) {
-      // need to find CRLF in buffer
-      if ((pos = buffer.find("\r\n")) == std::string::npos) return false;
-      std::string line(buffer.begin(), buffer.begin()+pos); // exclude CRLF
-      buffer.erase(buffer.begin(), buffer.begin()+pos+2); // remove line from buffer including CRLF
-      if (state == 0) { // startup line
-        std::string ver;
-        std::istringstream iss(line);
-        iss >> ver >> response->status >> response->statusText;
-        if (ver.find("HTTP/1.") != 0)
-          throw ParseError("Not a HTTP 1.x response");
-        state = 1;
-      } else if (state == 1) {
-        std::string key,value;
-        size_t pos;
-        if (line.empty()) {
-          chunked = (response->headers.find("transfer-encoding") != response->headers.end() && response->headers["transfer-encoding"] == "chunked");
-          state = 2;
-          break;
-        }
-        // split headers
-        if ((pos = line.find_first_of(": ")) == std::string::npos)
-          throw ParseError("Malformed header line");
-        key = line.substr(0, pos);
-        value = line.substr(pos+2);
-        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
-        response->headers[key] = value;
-      }
+  
+  void HTTPBase::write(std::ostream& os) const {
+    if (kind == YAHTTP_TYPE_REQUEST) {
+      std::ostringstream getparmbuf;
+      std::string getparms;
+      // prepare URL 
+      for(strstr_map_t::const_iterator i = getvars.begin(); i != getvars.end(); i++) {
+        getparmbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
+      }
+      if (getparmbuf.str().length() > 0)  
+        getparms = "?" + std::string(getparmbuf.str().begin(), getparmbuf.str().end() - 1);
+      else
+        getparms = "";
+      os << method << " " << url.path << getparms << " HTTP/1.1";
+    } else if (kind == YAHTTP_TYPE_RESPONSE) {
+      os << "HTTP/1.1 " << status << " ";
+      if (statusText.empty())
+        os << Utility::status2text(status);
+      else
+        os << statusText;
     }
-
-    if (buffer.size() == 0) return false;
-      
-    while(buffer.size() > 0) {
-      char buf[1024] = {0};
-
-      if (chunked) {
-        if (chunk_size == 0) {
-          // read chunk length
-          if ((pos = buffer.find('\n')) == std::string::npos) return false;
-          if (pos > 1023) 
-            throw ParseError("Impossible chunk_size");
-          buffer.copy(buf, pos);
-          buf[pos]=0; // just in case...
-          buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
-          sscanf(buf, "%x", &chunk_size);
-          if (!chunk_size) break; // last chunk
+    os << "\r\n";
+  
+    // write headers
+    strstr_map_t::const_iterator iter = headers.begin();
+    while(iter != headers.end()) {
+      if (iter->first == "host" && kind != YAHTTP_TYPE_REQUEST) { iter++; continue; }
+      os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
+      iter++;
+    }
+    if (jar.cookies.size() > 0) { // write cookies
+      for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
+        if (kind == YAHTTP_TYPE_REQUEST) {
+          os << "Cookie: ";
         } else {
-          if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
-          if (buffer.at(chunk_size) != '\n') return false; // there should be newline.
-          buffer.copy(buf, chunk_size);
-          buffer.erase(buffer.begin(), buffer.begin()+chunk_size+1);
-          bodybuf << buf;
-          chunk_size = 0;
-          if (buffer.size() == 0) return false; // just in case
+          os << "Set-Cookie: ";
         }
-      } else {
-        bodybuf << buffer;
-        buffer = "";
+        os << i->second.str() << "\r\n";
       }
     }
-
-    if (chunk_size!=0) return false; // need more data
-
-    bodybuf.flush();
-    response->body = bodybuf.str();
-    bodybuf.str("");
-    return true;
+    os << "\r\n";
+#ifdef HAVE_CPP_FUNC_PTR
+    this->renderer(this, os);
+#else
+    os << body;
+#endif
+  };
+  
+  std::ostream& operator<<(std::ostream& os, const Response &resp) {
+    resp.write(os);
+    return os;
+  };
+  
+  std::istream& operator>>(std::istream& is, Response &resp) {
+    YaHTTP::AsyncResponseLoader arl;
+    arl.initialize(&resp);
+    while(is.good()) {
+      char buf[1024];
+      is.read(buf, 1024);
+      if (is.gcount()) { // did we actually read anything
+        is.clear();
+        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
+      }
+    }
+    // throw unless ready
+    if (arl.ready() == false) 
+      throw ParseError("Was not able to extract a valid Response from stream");
+    arl.finalize();
+    return is;
+  };
+  
+  std::ostream& operator<<(std::ostream& os, const Request &req) {
+    req.write(os);
+    return os;
+  };
+  
+  std::istream& operator>>(std::istream& is, Request &req) {
+    YaHTTP::AsyncRequestLoader arl;
+    arl.initialize(&req);
+    while(is.good()) {
+      char buf[1024];
+      is.read(buf, 1024);
+      if (is.gcount()) { // did we actually read anything
+        is.clear();
+        if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
+      }
+    }
+    if (arl.ready() == false)
+      throw ParseError("Was not able to extract a valid Request from stream");
+    arl.finalize();
+    return is;
   };
 };
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/reqresp.hpp pdns-recursor-3.6.2/ext/yahttp/yahttp/reqresp.hpp
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/reqresp.hpp	2014-09-09 10:56:07.000000000 +0200
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/reqresp.hpp	2014-09-16 20:26:07.000000000 +0200
@@ -1,108 +1,278 @@
-#include <map>
-#include <iostream>
-#include <locale>
-#include <algorithm>
-
-#include "url.hpp"
-#include "utility.hpp"
-#include "exception.hpp"
+#ifdef HAVE_CXX11
+#include <functional>
+#define HAVE_CPP_FUNC_PTR
+namespace funcptr = std;
+#else
+#ifdef HAVE_BOOST
+#include <boost/function.hpp>
+namespace funcptr = boost;
+#define HAVE_CPP_FUNC_PTR
+#endif
+#endif
+
+#include <fstream>
+#include <cctype>
+
+#ifndef WIN32
+#include <cstdio>
+#include <unistd.h>
+#endif
 
-#ifndef YAHTTP_MAX_REQUEST_SIZE 
+#ifndef YAHTTP_MAX_REQUEST_SIZE
 #define YAHTTP_MAX_REQUEST_SIZE 2097152
 #endif
 
-namespace YaHTTP {
-  typedef std::map<std::string,std::string> strstr_map_t;
+#ifndef YAHTTP_MAX_RESPONSE_SIZE
+#define YAHTTP_MAX_RESPONSE_SIZE 2097152
+#endif
 
-  class Response;
-  class Request;
-  class AsyncResponseLoader;
-  class AsyncRequestLoader;
+#define YAHTTP_TYPE_REQUEST 1
+#define YAHTTP_TYPE_RESPONSE 2
 
-  class Request {
-  public:
-     Request();
-     Request(const Response &resp);
-     Request(const Request &req);
-     ~Request();
+namespace YaHTTP {
+  typedef std::map<std::string,std::string> strstr_map_t; //<! String to String map 
+  typedef std::map<std::string,Cookie> strcookie_map_t; //<! String to Cookie map
 
-     void build(const std::string &method, const std::string &url, const std::string &params);
+  typedef enum {
+    urlencoded,
+    multipart
+  } postformat_t; //<! Enumeration of possible post encodings, url encoding or multipart
 
-     void load(std::istream &is);
-     void write(std::ostream &os) const;
+  /*! Base class for request and response */
+  class HTTPBase {
+  public:
+#ifdef HAVE_CPP_FUNC_PTR
+    /*! Default renderer for request/response, simply copies body to response */
+    class SendBodyRender {
+    public:
+      SendBodyRender() {};
+
+      size_t operator()(const HTTPBase *doc, std::ostream& os) const {
+        os << doc->body;
+        return doc->body.length();
+      }; //<! writes body to ostream and returns length 
+    };
+    /* Simple sendfile renderer which streams file to ostream */
+    class SendFileRender {
+    public:
+      SendFileRender(const std::string& path) {
+        this->path = path;
+      };
+  
+      size_t operator()(const HTTPBase *doc __attribute__((unused)), std::ostream& os) const {
+        char buf[4096];
+        size_t n,k;
+#ifdef HAVE_CXX11
+        std::ifstream ifs(path, std::ifstream::binary);
+#else
+        std::ifstream ifs(path.c_str(), std::ifstream::binary);
+#endif
+        n = 0;
+        while(ifs && ifs.good()) {
+          ifs.read(buf, sizeof buf);
+          n += (k = ifs.gcount());
+          if (k)
+            os.write(buf, k);
+        }
 
-     strstr_map_t headers;
-     strstr_map_t parameters;
-     strstr_map_t cookies;
+        return n;
+      }; //<! writes file to ostream and returns length
 
-     URL url;
-     std::string method;
-     std::string body;
+      std::string path; //<! File to send
+    };
+#endif
+    HTTPBase() {
+      kind = 0;
+      status = 0;
+#ifdef HAVE_CPP_FUNC_PTR
+      renderer = SendBodyRender();
+#endif
+      max_request_size = YAHTTP_MAX_REQUEST_SIZE;
+      max_response_size = YAHTTP_MAX_RESPONSE_SIZE;
+    };
+protected:
+    HTTPBase(const HTTPBase& rhs) {
+      this->url = rhs.url; this->kind = rhs.kind;
+      this->status = rhs.status; this->statusText = rhs.statusText;
+      this->method = rhs.method; this->headers = rhs.headers;
+      this->jar = rhs.jar; this->postvars = rhs.postvars;
+      this->parameters = rhs.parameters; this->getvars = rhs.getvars;
+      this->body = rhs.body; this->max_request_size = rhs.max_request_size;
+      this->max_response_size = rhs.max_response_size;
+#ifdef HAVE_CPP_FUNC_PTR
+      this->renderer = rhs.renderer;
+#endif
+    };
+    HTTPBase& operator=(const HTTPBase& rhs) {
+      this->url = rhs.url; this->kind = rhs.kind;
+      this->status = rhs.status; this->statusText = rhs.statusText;
+      this->method = rhs.method; this->headers = rhs.headers;
+      this->jar = rhs.jar; this->postvars = rhs.postvars;
+      this->parameters = rhs.parameters; this->getvars = rhs.getvars;
+      this->body = rhs.body; this->max_request_size = rhs.max_request_size;
+      this->max_response_size = rhs.max_response_size;
+#ifdef HAVE_CPP_FUNC_PTR
+      this->renderer = rhs.renderer;
+#endif
+      return *this;
+    };
+public:
+    URL url; //<! URL of this request/response
+    int kind; //<! Type of object (1 = request, 2 = response)
+    int status; //<! status code 
+    std::string statusText; //<! textual representation of status code
+    std::string method; //<! http verb
+    strstr_map_t headers; //<! map of header(s)
+    CookieJar jar; //<! cookies 
+    strstr_map_t postvars; //<! map of POST variables (from POST body)
+    strstr_map_t getvars; //<! map of GET variables (from URL)
+// these two are for Router
+    strstr_map_t parameters; //<! map of route parameters (only if you use YaHTTP::Router)
+    std::string routeName; //<! name of the current route (only if you use YaHTTP::Router)
+
+    std::string body; //<! the actual content
+
+    ssize_t max_request_size; //<! maximum size of request
+    ssize_t max_response_size;  //<! maximum size of response
+ 
+#ifdef HAVE_CPP_FUNC_PTR
+    funcptr::function<size_t(const HTTPBase*,std::ostream&)> renderer; //<! rendering function
+#endif
+    void write(std::ostream& os) const; //<! writes request to the given output stream
 
-     friend std::istream& operator>>(std::istream& os, const Request &req);
-     friend std::ostream& operator<<(std::ostream& os, const Request &req);
-     friend class AsyncRequestLoader;
+    strstr_map_t& GET() { return getvars; }; //<! acccessor for getvars
+    strstr_map_t& POST() { return postvars; }; //<! accessor for postvars
+    strcookie_map_t& COOKIES() { return jar.cookies; }; //<! accessor for cookies
+
+    std::string str() const {
+       std::ostringstream oss;
+       write(oss);
+       return oss.str();
+    }; //<! return string representation of this object
   };
 
-  class Response {
+  /*! Response class, represents a HTTP Response document */
+  class Response: public HTTPBase { 
   public:
-     Response();
-     Response(const Request &req);
-     Response(const Response &resp);
-     ~Response();
-     void load(std::istream &is);
-     void write(std::ostream &os) const;
-
-     strstr_map_t headers;
-     strstr_map_t parameters;
-     strstr_map_t cookies;
-
-     URL url;
-     int status;
-     std::string statusText;
-     std::string method;
-     std::string body;
-
-     friend std::istream& operator>>(std::istream& is, Response &resp);
-     friend std::ostream& operator<<(std::ostream& os, const Response &resp);
-     friend class AsyncResponseLoader;
+    Response() { this->kind = YAHTTP_TYPE_RESPONSE; };
+    Response(const HTTPBase& rhs): HTTPBase(rhs) {
+      this->kind = YAHTTP_TYPE_RESPONSE;
+    };
+    Response& operator=(const HTTPBase& rhs) {
+      HTTPBase::operator=(rhs);
+      this->kind = YAHTTP_TYPE_RESPONSE;
+      return *this;
+    };
+    friend std::ostream& operator<<(std::ostream& os, const Response &resp);
+    friend std::istream& operator>>(std::istream& is, Response &resp);
   };
 
-  class AsyncResponseLoader {
+  /* Request class, represents a HTTP Request document */
+  class Request: public HTTPBase {
   public:
-    AsyncResponseLoader(Response *response) {
-      state = 0;
-      chunked = false;
-      chunk_size = 0;
-      this->response = response;
-    };
-    bool feed(const std::string &somedata);
-  private:
-    Response *response;
-    int state;
-    std::string buffer;
-    bool chunked;
-    int chunk_size;
-    std::ostringstream bodybuf;
+    Request() { this->kind = YAHTTP_TYPE_REQUEST; };
+    Request(const HTTPBase& rhs): HTTPBase(rhs) {
+      this->kind = YAHTTP_TYPE_REQUEST;
+    };
+    Request& operator=(const HTTPBase& rhs) {
+      HTTPBase::operator=(rhs);
+      this->kind = YAHTTP_TYPE_REQUEST;
+      return *this;
+    };
+
+    void setup(const std::string& method, const std::string& url) {
+      this->url.parse(url);
+      this->headers["host"] = this->url.host;
+      this->method = method;
+      std::transform(this->method.begin(), this->method.end(), this->method.begin(), ::toupper);
+      this->headers["user-agent"] = "YaHTTP v1.0";
+    }; //<! Set some initial things for a request
+
+    void preparePost(postformat_t format = urlencoded) {
+      std::ostringstream postbuf;
+      if (format == urlencoded) {
+        for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
+          postbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
+        }
+        // remove last bit
+        if (postbuf.str().length()>0) 
+          body = postbuf.str().substr(0, postbuf.str().length()-1);
+        else
+          body = "";
+        headers["content-type"] = "application/x-www-form-urlencoded; charset=utf-8";
+      } else if (format == multipart) {
+        headers["content-type"] = "multipart/form-data; boundary=YaHTTP-12ca543";
+        for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
+          postbuf << "--YaHTTP-12ca543\r\nContent-Disposition: form-data; name=\"" << Utility::encodeURL(i->first, false) << "; charset=UTF-8\r\n\r\n"
+            << Utility::encodeURL(i->second, false) << "\r\n";
+        }
+      }
+
+      postbuf.str("");
+      postbuf << body.length();
+      // set method and change headers
+      method = "POST";
+      headers["content-length"] = postbuf.str();
+    }; //<! convert all postvars into string and stuff it into body
+
+    friend std::ostream& operator<<(std::ostream& os, const Request &resp);
+    friend std::istream& operator>>(std::istream& is, Request &resp);
   };
 
-  class AsyncRequestLoader {
+  /*! Asynchronous HTTP document loader */
+  template <class T>
+  class AsyncLoader {
   public:
-    AsyncRequestLoader(Request *request) {
-      state = 0;
-      chunked = false;
-      chunk_size = 0;
-      maxbody = 0;
-      this->request = request;
-    };
-    bool feed(const std::string &somedata);
-  private:
-    Request *request;
-    int state;
-    std::string buffer;
-    bool chunked;
-    int chunk_size;
-    std::ostringstream bodybuf;
-    long maxbody;
+    T* target; //<! target to populate
+    int state; //<! reader state
+    size_t pos; //<! reader position
+    
+    std::string buffer; //<! read buffer 
+    bool chunked; //<! whether we are parsing chunked data
+    int chunk_size; //<! expected size of next chunk
+    std::ostringstream bodybuf; //<! buffer for body
+    size_t maxbody; //<! maximum size of body
+    size_t minbody; //<! minimum size of body
+    bool hasBody; //<! are we expecting body
+
+    void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value); //<! key value pair parser helper
+
+    void initialize(T* target) {
+      chunked = false; chunk_size = 0;
+      bodybuf.str(""); maxbody = 0;
+      pos = 0; state = 0; this->target = target; 
+      hasBody = false;
+    }; //<! Initialize the parser for target and clear state
+    int feed(const std::string& somedata); //<! Feed data to the parser
+    bool ready() {
+     return (chunked == true && state == 3) || // if it's chunked we get end of data indication
+             (chunked == false && state > 1 &&  
+               (!hasBody || 
+                 (bodybuf.str().size() <= maxbody && 
+                  bodybuf.str().size() >= minbody)
+               )
+             ); 
+    }; //<! whether we have received enough data
+    void finalize() {
+      bodybuf.flush();
+      if (ready()) {
+        strstr_map_t::iterator pos = target->headers.find("content-type");
+        if (pos != target->headers.end() && Utility::iequals(pos->second, "application/x-www-form-urlencoded", 32)) {
+          target->postvars = Utility::parseUrlParameters(bodybuf.str());
+        }
+        target->body = bodybuf.str();
+      }
+      bodybuf.str("");
+      this->target = NULL;
+    }; //<! finalize and release target
+  };
+
+  /*! Asynchronous HTTP response loader */
+  class AsyncResponseLoader: public AsyncLoader<Response> {
+  };
+
+  /*! Asynchronous HTTP request loader */
+  class AsyncRequestLoader: public AsyncLoader<Request> {
   };
+
 };
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/router.cpp pdns-recursor-3.6.2/ext/yahttp/yahttp/router.cpp
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/router.cpp	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/router.cpp	2014-09-16 20:26:07.000000000 +0200
@@ -0,0 +1,163 @@
+/* @file
+ * @brief Concrete implementation of Router 
+ */
+#include "yahttp.hpp"
+#include "router.hpp"
+
+namespace YaHTTP {
+  typedef funcptr::tuple<int,int> TDelim;
+
+  // router is defined here.
+  YaHTTP::Router Router::router;
+
+  void Router::map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name) {
+    std::string method2 = method;
+    bool isopen=false;
+    // add into vector
+    for(std::string::const_iterator i = url.begin(); i != url.end(); i++) {
+       if (*i == '<' && isopen) throw Error("Invalid URL mask, cannot have < after <");
+       if (*i == '<') isopen = true;
+       if (*i == '>' && !isopen) throw Error("Invalid URL mask, cannot have > without < first");
+       if (*i == '>') isopen = false;
+    }
+    std::transform(method2.begin(), method2.end(), method2.begin(), ::toupper); 
+    routes.push_back(funcptr::make_tuple(method2, url, handler, name));
+  };
+
+  bool Router::route(Request *req, THandlerFunction& handler) {
+    std::map<std::string, TDelim> params;
+    int pos1,pos2;
+    std::string pname;
+    bool matched = false;
+    std::string rname;
+
+    // iterate routes
+    for(TRouteList::iterator i = routes.begin(); !matched && i != routes.end(); i++) {
+      int k1,k2,k3;
+      std::string pname;
+      std::string method, url;
+      funcptr::tie(method, url, handler, rname) = *i;
+    
+      if (method.empty() == false && req->method != method) continue; // no match on method
+      // see if we can't match the url
+      params.clear();
+      // simple matcher func
+      for(k1=0, k2=0; k1 < static_cast<int>(url.size()) && k2 < static_cast<int>(req->url.path.size()); ) {
+        if (url[k1] == '<') {
+          pos1 = k2;
+          k3 = k1+1;
+          // start of parameter
+          while(k1 < static_cast<int>(url.size()) && url[k1] != '>') k1++;
+          pname = std::string(url.begin()+k3, url.begin()+k1);
+          // then we also look it on the url
+          if (pname[0]=='*') {
+            pname = pname.substr(1);
+            // this matches whatever comes after it, basically end of string
+            pos2 = req->url.path.size();
+            matched = true;
+            if (pname != "") 
+              params[pname] = funcptr::tie(pos1,pos2);
+            k1 = url.size();
+            k2 = req->url.path.size();
+            break;
+          } else { 
+            // match until url[k1]
+            while(k2 < static_cast<int>(req->url.path.size()) && req->url.path[k2] != url[k1+1]) k2++;
+            pos2 = k2;
+            params[pname] = funcptr::tie(pos1,pos2);
+          }
+          k2--;
+        }
+        else if (url[k1] != req->url.path[k2]) {
+          break;
+        }
+
+        k1++; k2++;
+      }
+
+      // ensure.
+      if (url[k1] != req->url.path[k2]) 
+        matched = false;
+      else
+        matched = true;
+    }
+
+    if (!matched) { return false; } // no route
+    req->parameters.clear();    
+
+    for(std::map<std::string, TDelim>::iterator i = params.begin(); i != params.end(); i++) {
+      int p1,p2;
+      funcptr::tie(p1,p2) = i->second;
+      std::string value(req->url.path.begin() + p1, req->url.path.begin() + p2);
+      value = Utility::decodeURL(value);
+      req->parameters[i->first] = value;
+    }
+
+    req->routeName = rname;
+
+    return true;
+  };
+
+  void Router::printRoutes(std::ostream &os) {
+    for(TRouteList::iterator i = routes.begin(); i != routes.end(); i++) {
+#ifdef HAVE_CXX11
+      std::streamsize ss = os.width();
+      std::ios::fmtflags ff = os.setf(std::ios::left);
+      os.width(10);
+      os << std::get<0>(*i);
+      os.width(50);
+      os << std::get<1>(*i);
+      os.width(ss);
+      os.setf(ff);
+      os << "    " << std::get<3>(*i);
+      os << std::endl;
+#else
+      os << i->get<0>() << "    " << i->get<1>() << "    " << i->get<3>() << std::endl;
+#endif
+    } 
+  };
+
+  std::pair<std::string,std::string> Router::urlFor(const std::string &name, const strstr_map_t& arguments) {
+    std::ostringstream path;
+    std::string mask,method,result;
+    int k1,k2,k3;
+
+    bool found = false;
+    for(TRouteList::iterator i = routes.begin(); !found && i != routes.end(); i++) {
+#ifdef HAVE_CXX11
+      if (std::get<3>(*i) == name) { mask = std::get<1>(*i); method = std::get<0>(*i); found = true; }
+#else
+      if (i->get<3>() == name) { mask = i->get<1>(); method = i->get<0>(); found = true; }
+#endif
+    }
+
+    if (!found)
+      throw Error("Route not found");
+
+    for(k1=0,k3=0;k1<static_cast<int>(mask.size());k1++) {
+      if (mask[k1] == '<') {
+        std::string pname;
+        strstr_map_t::const_iterator pptr;
+        k2=k1;
+        while(k1<static_cast<int>(mask.size()) && mask[k1]!='>') k1++;
+        path << mask.substr(k3,k2-k3);
+        if (mask[k2+1] == '*')
+          pname = std::string(mask.begin() + k2 + 2, mask.begin() + k1);
+        else 
+          pname = std::string(mask.begin() + k2 + 1, mask.begin() + k1);
+        if ((pptr = arguments.find(pname)) != arguments.end()) 
+          path << Utility::encodeURL(pptr->second);
+        k3 = k1+1;
+      }
+      else if (mask[k1] == '*') {
+        // ready 
+        k3++;
+        continue;
+      }
+    }
+    std::cout << mask.substr(k3) << std::endl;
+    path << mask.substr(k3);
+    result = path.str();
+    return std::make_pair(method, result);
+  }
+};
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/router.hpp pdns-recursor-3.6.2/ext/yahttp/yahttp/router.hpp
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/router.hpp	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/router.hpp	2014-09-16 20:26:07.000000000 +0200
@@ -0,0 +1,74 @@
+#ifndef _YAHTTP_ROUTER_HPP
+#define _YAHTTP_ROUTER_HPP 1
+/* @file 
+ * @brief Defines router class and support structures
+ */
+#ifdef HAVE_CXX11
+#include <functional>
+#include <tuple>
+#define HAVE_CPP_FUNC_PTR
+#define IGNORE std::ignore
+namespace funcptr = std;
+#else
+#ifdef HAVE_BOOST
+#include <boost/function.hpp>
+#include <boost/tuple/tuple.hpp>
+#define IGNORE boost::tuples::ignore
+namespace funcptr = boost;
+#define HAVE_CPP_FUNC_PTR
+#else
+#warn "You need to configure with boost or have C++11 capable compiler for router"
+#endif
+#endif
+
+#ifdef HAVE_CPP_FUNC_PTR
+#include <vector>
+#include <utility>
+
+namespace YaHTTP {
+  typedef funcptr::function <void(Request* req, Response* resp)> THandlerFunction; //!< Handler function pointer 
+  typedef funcptr::tuple<std::string, std::string, THandlerFunction, std::string> TRoute; //!< Route tuple (method, urlmask, handler, name)
+  typedef std::vector<TRoute> TRouteList; //!< List of routes in order of evaluation
+
+  /*! Implements simple router.
+
+This class implements a router for masked urls. The URL mask syntax is as of follows
+
+/&lt;masked&gt;/url&lt;number&gt;/&lt;hi&gt;.&lt;format&gt;
+
+You can use &lt;*param&gt; to denote that everything will be matched and consumed into the parameter, including slash (/). Use &lt;*&gt; to denote that URL 
+is consumed but not stored. Note that only path is matched, scheme, host and url parameters are ignored. 
+   */
+  class Router {
+  private:
+    Router() {}; 
+    static Router router; //<! Singleton instance of Router
+  public:
+    void map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name); //<! Instance method for mapping urls
+    bool route(Request *req, THandlerFunction& handler); //<! Instance method for performing routing
+    void printRoutes(std::ostream &os); //<! Instance method for printing routes
+    std::pair<std::string, std::string> urlFor(const std::string &name, const strstr_map_t& arguments); //<! Instance method for generating paths
+
+/*! Map an URL.
+If method is left empty, it will match any method. Name is also optional, but needed if you want to find it for making URLs 
+*/
+    static void Map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map(method, url, handler, name); }; 
+    static void Get(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("GET", url, handler, name); }; //<! Helper for mapping GET
+    static void Post(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("POST", url, handler, name); }; //<! Helper for mapping POST
+    static void Put(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("PUT", url, handler, name); }; //<! Helper for mapping PUT
+    static void Patch(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("PATCH", url, handler, name); }; //<! Helper for mapping PATCH
+    static void Delete(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("DELETE", url, handler, name); }; //<! Helper for mapping DELETE
+    static void Any(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("", url, handler, name); }; //<! Helper for mapping any method
+
+    static bool Route(Request *req, THandlerFunction& handler) { return router.route(req, handler); }; //<! Performs routing based on req->url.path 
+    static void PrintRoutes(std::ostream &os) { router.printRoutes(os); }; //<! Prints all known routes to given output stream
+
+    static std::pair<std::string, std::string> URLFor(const std::string &name, const strstr_map_t& arguments) { return router.urlFor(name,arguments); }; //<! Generates url from named route and arguments. Missing arguments are assumed empty
+    static const TRouteList& GetRoutes() { return router.routes; } //<! Reference to route list 
+
+    TRouteList routes; //<! Instance variable for routes
+  };
+};
+#endif
+
+#endif
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/url.hpp pdns-recursor-3.6.2/ext/yahttp/yahttp/url.hpp
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/url.hpp	2014-09-09 10:56:07.000000000 +0200
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/url.hpp	2014-09-16 20:26:07.000000000 +0200
@@ -10,6 +10,7 @@
 #endif 
 
 namespace YaHTTP {
+  /*! URL parser and container */
   class URL {
    private: 
       bool parseSchema(const std::string& url, size_t &pos) {
@@ -25,7 +26,7 @@
              pos += 2;
           }
           return true;
-      }
+      }; //<! parse schema/protocol part 
 
       bool parseHost(const std::string& url, size_t &pos) {
           size_t pos1;
@@ -38,8 +39,13 @@
              host = url.substr(pos, pos1-pos);
              pos = pos1;
           }
+          if ( (pos1 = host.find_first_of(":")) != std::string::npos ) {
+             std::istringstream tmp(host.substr(pos1+1));
+             tmp >> port;
+             host = host.substr(0, pos1);
+          }
           return true;
-      }
+      }; //<! parse host and port
 
       bool parseUserPass(const std::string& url, size_t &pos) {
           size_t pos1,pos2;
@@ -58,22 +64,21 @@
           pos = pos1+1;
           username = Utility::decodeURL(username);
           return true;
-      };
+      }; //<! parse possible username and password
 
       bool parsePath(const std::string& url, size_t &pos) {
           size_t pos1;
           if (pos >= url.size()) return true; // no data
           if (url[pos] != '/') return false; // not an url
           if ( (pos1 = url.find_first_of("?", pos)) == std::string::npos ) {
-             path = url;
+             path = url.substr(pos);
              pos = url.size();
           } else {
              path = url.substr(pos, pos1-pos);
              pos = pos1;
           }
-          path = Utility::decodeURL(path);
           return true;
-      }
+      }; //<! parse path component
 
       bool parseParameters(const std::string& url, size_t &pos) {
           size_t pos1;
@@ -87,15 +92,20 @@
              parameters = url.substr(pos+1, pos1-pos-1);
              pos = pos1;
           }
+          if (parameters.size()>0 && *(parameters.end()-1) == '&') parameters.resize(parameters.size()-1);
           return true;
-      }
+      }; //<! parse url parameters
 
       bool parseAnchor(const std::string& url, size_t &pos) {
           if (pos >= url.size()) return true; // no data
           if (url[pos] != '#') return false; // not anchor
           anchor = url.substr(pos+1);
           return true;
-      }
+      }; //<! parse anchor
+
+      void initialize() {
+        protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true;
+      }; //<! initialize to empty URL
 
   public:
       std::string to_string() const {
@@ -122,7 +132,7 @@
               port > 0) 
             oss << ":" << port;
 
-          oss << Utility::encodeURL(path, true);
+          oss << path;
           if (parameters.empty() == false) {
              if (!pathless) 
                 oss << "?";
@@ -131,53 +141,51 @@
           if (anchor.empty() == false)
              oss << "#" << anchor;
           return oss.str();
-      }
+      }; //<! convert this URL to string
 
-      std::string protocol;
-      std::string host;
-      int port;
-      std::string username;
-      std::string password;
-      std::string path;
-      std::string parameters;
-      std::string anchor;
-      bool pathless;
+      std::string protocol; //<! schema/protocol 
+      std::string host; //<! host
+      int port; //<! port
+      std::string username; //<! username
+      std::string password; //<! password
+      std::string path; //<! path 
+      std::string parameters; //<! url parameters
+      std::string anchor; //<! anchor
+      bool pathless; //<! whether this url has no path
 
-      URL() { protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true; };
+      URL() { initialize(); }; //<! construct empty url
       URL(const std::string& url) {
-          parse(url);
-      };
+        parse(url);
+      }; //<! calls parse with url 
 
       URL(const char *url) {
-          parse(std::string(url));
-      };
+        parse(std::string(url));
+      }; //<! calls parse with url
 
       bool parse(const std::string& url) {
-          // setup
-          protocol = ""; host = ""; port = 0; 
-          username = ""; password = ""; path = ""; 
-          parameters = ""; anchor =""; pathless = true;
-
-          if (url.size() > YAHTTP_MAX_URL_LENGTH) return false;
-          size_t pos = 0;
-          if (*(url.begin()) != '/') { // full url?
-             if (parseSchema(url, pos) == false) return false;
-             if (pathless) {
-                parameters = url.substr(pos);
-                return true;
-             }
-             if (parseUserPass(url, pos) == false) return false;
-             if (parseHost(url, pos) == false) return false;
-          }
-          if (parsePath(url, pos) == false) return false;
-          if (parseParameters(url, pos) == false) return false;
-          return parseAnchor(url, pos);
-      };
-
-      friend std::ostream & operator<<(std::ostream& os, const URL& url) {
-         os<<url.to_string();
-         return os;
-      }
+        // setup
+        initialize();
+
+        if (url.size() > YAHTTP_MAX_URL_LENGTH) return false;
+        size_t pos = 0;
+        if (*(url.begin()) != '/') { // full url?
+          if (parseSchema(url, pos) == false) return false;
+          if (pathless) {
+            parameters = url.substr(pos);
+            return true;
+          }
+          if (parseUserPass(url, pos) == false) return false;
+          if (parseHost(url, pos) == false) return false;
+        }
+        if (parsePath(url, pos) == false) return false;
+        if (parseParameters(url, pos) == false) return false;
+        return parseAnchor(url, pos);
+    }; //<! parse various formats of urls ranging from http://example.com/foo?bar=baz into data:base64:d089swt64wt... 
+
+    friend std::ostream & operator<<(std::ostream& os, const URL& url) {
+      os<<url.to_string();
+      return os;
+    };
   };
 };
 #endif
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/utility.hpp pdns-recursor-3.6.2/ext/yahttp/yahttp/utility.hpp
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/utility.hpp	2014-09-09 10:56:07.000000000 +0200
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/utility.hpp	2014-09-16 20:26:07.000000000 +0200
@@ -1,11 +1,189 @@
 #ifndef _YAHTTP_UTILITY_HPP
 #define _YAHTTP_UTILITY_HPP 1
 
-#include <string>
-#include <algorithm>
-#include <cstdio>
-
 namespace YaHTTP {
+  static const char *MONTHS[] = {0,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",0}; //<! List of months 
+  static const char *DAYS[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat",0}; //<! List of days
+
+  /*! Represents a date/time with utc offset */
+  class DateTime {
+  public:
+     bool isSet; //<! if this is initialized yet
+
+     int year; //<! year, 0 is year 0, not 1900
+
+     int month; //<! month, range 1-12
+     int day; //<! day, range 1-31
+     int wday; //<! week day, range 1-7
+
+     int hours; //<! hours, range 0-23
+     int minutes; //<! minutes, range 0-59
+     int seconds; //<! seconds, range 0-60
+
+     int utc_offset; //<! UTC offset with minutes (hhmm)
+
+     DateTime() { 
+       initialize();
+     }; //<! Construct and initialize
+
+     void initialize() {
+       isSet = false; 
+       year = month = day = wday = hours = minutes = seconds = utc_offset = 0;
+       month = 1; // it's invalid otherwise
+     }; //<! Creates year 0 date
+
+     void setLocal() {
+       fromLocaltime(time((time_t*)NULL)); 
+     }; //<! sets current local time
+
+     void setGm() {
+       fromGmtime(time((time_t*)NULL));
+     }; //<! sets current gmtime (almost UTC)
+
+     void fromLocaltime(time_t t) {
+#ifdef HAVE_LOCALTIME_R
+       struct tm tm;
+       localtime_r(&t, &tm);
+       fromTm(&tm);
+#else
+       struct tm *tm;
+       tm = localtime(&t);
+       fromTm(tm);
+#endif
+#ifndef HAVE_TM_GMTOFF
+       time_t t2;
+# ifdef HAVE_LOCALTIME_R
+       gmtime_r(&t, &tm);
+       t2 = mktime(&tm);
+# else
+       tm = gmtime(&t);
+       t2 = mktime(tm);
+# endif
+       this->utc_offset = ((t2-t)/10)*10; // removes any possible differences. 
+#endif
+     }; //<! uses localtime for time
+
+     void fromGmtime(time_t t) {
+#ifdef HAVE_GMTIME_R
+       struct tm tm;
+       gmtime_r(&t, &tm);
+       fromTm(&tm);
+#else
+       struct tm *tm;
+       tm = gmtime(&t);
+       fromTm(tm);
+#endif
+#ifndef HAVE_TM_GMTOFF
+       this->utc_offset = 0;
+#endif
+     }; //<! uses gmtime for time
+
+     void fromTm(const struct tm *tm) {
+       year = tm->tm_year + 1900;
+       month = tm->tm_mon + 1;
+       day = tm->tm_mday;
+       hours = tm->tm_hour;
+       minutes = tm->tm_min;
+       seconds = tm->tm_sec;
+       wday = tm->tm_wday;
+#ifdef HAVE_TM_GMTOFF
+       utc_offset = tm->tm_gmtoff;
+#endif
+       isSet = true;
+     }; //<! parses date from struct tm 
+
+     void validate() const {
+       if (wday < 0 || wday > 6) throw "Invalid date";
+       if (month < 1 || month > 12) throw "Invalid date";
+       if (year < 0) throw "Invalid date";
+       if (hours < 0 || hours > 23 ||
+           minutes < 0 || minutes > 59 ||
+           seconds < 0 || seconds > 60) throw "Invalid date";
+     }; //<! make sure we are within ranges (not a *REAL* validation, just range check)
+
+     std::string rfc_str() const {
+       std::ostringstream oss;
+       validate();
+       oss << DAYS[wday] << ", " << std::setfill('0') << std::setw(2) << day << " " << MONTHS[month] << " " <<
+          std::setfill('0') << std::setw(2) <<  year << " " << 
+          std::setfill('0') << std::setw(2) << hours << ":" << 
+          std::setfill('0') << std::setw(2) << minutes << ":" << 
+          std::setfill('0') << std::setw(2) << seconds << " ";
+       if (utc_offset>=0) oss << "+";
+       else oss << "-";
+       int tmp_off = ( utc_offset < 0 ? utc_offset*-1 : utc_offset ); 
+       oss << std::setfill('0') << std::setw(2) << (tmp_off/3600);
+       oss << std::setfill('0') << std::setw(2) << (tmp_off%3600)/60;
+
+       return oss.str(); 
+     }; //<! converts this date into a RFC-822 format
+ 
+     std::string cookie_str() const {
+       std::ostringstream oss;
+       validate();
+       oss << std::setfill('0') << std::setw(2) << day << "-" << MONTHS[month] << "-" << year << " " <<
+         std::setfill('0') << std::setw(2) << hours << ":" << 
+         std::setfill('0') << std::setw(2) << minutes << ":" << 
+         std::setfill('0') << std::setw(2) << seconds << " GMT";
+       return oss.str();
+     }; //<! converts this date into a HTTP Cookie date
+ 
+     void parse822(const std::string &rfc822_date) {
+       struct tm tm;
+       const char *ptr;
+#ifdef HAVE_TM_GMTOFF
+       if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T %z", &tm)) != NULL) {
+#else
+	if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T", &tm)) != NULL) {
+          int sign;
+  	  // parse the timezone parameter
+          while(*ptr && ::isspace(*ptr)) ptr++;
+          if (*ptr == '+') sign = 0;
+          else if (*ptr == '-') sign = -1;
+          else throw "Unparseable date";
+          ptr++;
+          utc_offset = ::atoi(ptr) * sign;
+          while(*ptr && ::isdigit(*ptr)) ptr++;
+#endif
+          while(*ptr && ::isspace(*ptr)) ptr++;
+          if (*ptr) throw "Unparseable date"; // must be final.
+          fromTm(&tm);
+       } else {
+          throw "Unparseable date";
+       }
+     }; //<! parses RFC-822 date
+
+     void parseCookie(const std::string &cookie_date) {
+       struct tm tm;
+       const char *ptr;
+       if ( (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T", &tm)) != NULL) {
+          while(*ptr && ( ::isspace(*ptr) || ::isalnum(*ptr) )) ptr++;
+          std::cerr << ptr << std::endl;
+          if (*ptr) throw "Unparseable date (non-final)"; // must be final.
+          fromTm(&tm);
+          this->utc_offset = 0;
+       } else {
+          throw "Unparseable date (did not match pattern cookie)";
+       }
+     }; //<! parses HTTP Cookie date
+
+     time_t unixtime() const {
+       struct tm tm;
+       tm.tm_year = year-1900;
+       tm.tm_mon = month-1;
+       tm.tm_mday = day;
+       tm.tm_hour = hours;
+       tm.tm_min = minutes;
+       tm.tm_sec = seconds;
+       tm.tm_isdst = 0;
+#ifdef HAVE_TM_GMTOFF
+       tm.tm_gmtoff = utc_offset;
+#endif
+       return mktime(&tm);
+     }; //<! returns this datetime as unixtime. will not work for dates before 1970/1/1 00:00:00 GMT
+  };
+
+  /*! Various helpers needed in the code */ 
   class Utility {
   public:
     static std::string decodeURL(const std::string& component) {
@@ -35,23 +213,40 @@
            pos2=pos1;
         }
         return result;
-    };
+    }; //<! Decodes %xx from string into bytes
     
-    static std::string encodeURL(const std::string& component, bool encodeSlash = true) {
+    static std::string encodeURL(const std::string& component, bool asUrl = true) {
       std::string result = component;
+      std::string skip = "+-.:,&;_#%[]?/@(){}=";
       char repl[3];
       size_t pos;
       for(std::string::iterator iter = result.begin(); iter != result.end(); iter++) {
-        if (*iter != '+' && !(encodeSlash == false || *iter == '/') && !std::isalnum(*iter)) {
+        if (!std::isalnum(*iter) && (!asUrl || skip.find(*iter) == std::string::npos)) {
           // replace with different thing
           pos = std::distance(result.begin(), iter);
-          ::snprintf(repl,3,"%02x", *iter);
+          ::snprintf(repl,3,"%02x", static_cast<unsigned char>(*iter));
           result = result.replace(pos, 1, "%", 1).insert(pos+1, repl, 2);
           iter = result.begin() + pos + 2;
         }
       }
       return result;
-    };
+    }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url
+
+    static std::string encodeURL(const std::wstring& component, bool asUrl = true) {
+      unsigned char const *p = reinterpret_cast<unsigned char const*>(&component[0]);
+      std::size_t s = component.size() * sizeof((*component.begin()));
+      std::vector<unsigned char> vec(p, p+s);
+
+      std::ostringstream result;
+      std::string skip = "+-.,&;_#%[]?/@(){}=";
+      for(std::vector<unsigned char>::iterator iter = vec.begin(); iter != vec.end(); iter++) {
+        if (!std::isalnum((char)*iter) && (!asUrl || skip.find((char)*iter) == std::string::npos)) {
+          // bit more complex replace
+          result << "%" << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(*iter);
+        } else result << (char)*iter;
+      }
+      return result.str();
+    }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url, for wide strings, returns ordinary string
 
     static std::string status2text(int status) {
        switch(status) {
@@ -134,7 +329,7 @@
        default:
            return "Unknown Status";
        }
-    };
+    }; //<! static HTTP codes to text mappings
 
     static std::map<std::string,std::string> parseUrlParameters(std::string parameters) {
       std::string::size_type pos = 0;
@@ -173,14 +368,33 @@
         pos = nextpos+1;
       }
       return parameter_map;
-    };
+    }; //<! parses URL parameters into string map 
+
+    static bool iequals(const std::string& a, const std::string& b, size_t length) {
+      std::string::const_iterator ai, bi;
+      size_t i;
+      for(ai = a.begin(), bi = b.begin(), i = 0; ai != a.end() && bi != b.end() && i < length; ai++,bi++,i++) {
+        if (::toupper(*ai) != ::toupper(*bi)) return false;
+      }
+
+      if (ai == a.end() && bi == b.end()) return true;
+      if ((ai == a.end() && bi != b.end()) ||
+          (ai != a.end() && bi == b.end())) return false;
+      
+      return ::toupper(*ai) == ::toupper(*bi);
+    }; //<! case-insensitive comparison with length
+
+    static bool iequals(const std::string& a, const std::string& b) {
+      if (a.size() != b.size()) return false;
+      return iequals(a,b,a.size());
+    }; //<! case-insensitive comparison
 
-    static void trim_right(std::string &str) {
+    static void trimRight(std::string &str) {
        const std::locale &loc = std::locale::classic();
        std::string::reverse_iterator iter = str.rbegin();
        while(iter != str.rend() && std::isspace(*iter, loc)) iter++;
        str.erase(iter.base(), str.end());
-    };
+    }; //<! removes whitespace from right
 
     static std::string camelizeHeader(const std::string &str) {
        std::string::const_iterator iter = str.begin();
@@ -198,7 +412,7 @@
        }
 
        return result;
-     };
-   };
+   }; //<! camelizes headers, such as, content-type => Content-Type
+  };
 };
 #endif
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/yahttp-config.h pdns-recursor-3.6.2/ext/yahttp/yahttp/yahttp-config.h
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/yahttp-config.h	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/yahttp-config.h	2014-10-30 13:25:52.000000000 +0100
@@ -0,0 +1 @@
+#include "../../../config.h"
diff -Nru pdns-recursor-3.6.1/ext/yahttp/yahttp/yahttp.hpp pdns-recursor-3.6.2/ext/yahttp/yahttp/yahttp.hpp
--- pdns-recursor-3.6.1/ext/yahttp/yahttp/yahttp.hpp	2014-09-09 10:56:07.000000000 +0200
+++ pdns-recursor-3.6.2/ext/yahttp/yahttp/yahttp.hpp	2014-09-16 20:26:07.000000000 +0200
@@ -1,3 +1,36 @@
+#include <map>
+#include <iostream>
+#include <locale>
+#include <algorithm>
+#include <string>
+#include <cstdio>
+#include <sys/time.h>
+#include <iomanip>
+#include <list>
+#include <vector>
+
+#include "yahttp-config.h"
+#include "url.hpp"
 #include "utility.hpp"
+#include "exception.hpp"
 #include "url.hpp"
+#include "cookie.hpp"
 #include "reqresp.hpp"
+
+/*! \mainpage Yet Another HTTP Library Documentation
+\section sec_quick_start Quick start example
+
+@code
+#include <yahttp/yahttp.hpp>
+
+int main(void) {
+  std::ifstream ifs("request.txt");
+  YaHTTP::Request req;
+  ifs >> req;
+
+  std::cout << req.method " " << req.url.path << std::endl;
+  return 0;
+}
+@endcode
+\author Aki Tuomi
+*/
diff -Nru pdns-recursor-3.6.1/Makefile pdns-recursor-3.6.2/Makefile
--- pdns-recursor-3.6.1/Makefile	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/Makefile	2014-10-30 13:25:52.000000000 +0100
@@ -23,8 +23,8 @@
 dns_random.o ext/polarssl-1.3.2/library/aes.o ext/polarssl-1.3.2/library/padlock.o dnslabeltext.o \
 lua-pdns.o lua-recursor.o randomhelper.o recpacketcache.o dns.o \
 reczones.o base32.o nsecrecords.o json.o ws-recursor.o ws-api.o \
-version.o responsestats.o webserver.o ext/yahttp/yahttp/reqresp.o \
-rec-carbon.o
+version.o responsestats.o webserver.o ext/yahttp/yahttp/reqresp.o ext/yahttp/yahttp/router.o \
+rec-carbon.o secpoll-recursor.o
 
 REC_CONTROL_OBJECTS=rec_channel.o rec_control.o arguments.o misc.o \
 	unix_utility.o logger.o qtype.o
diff -Nru pdns-recursor-3.6.1/pdns_recursor.cc pdns-recursor-3.6.2/pdns_recursor.cc
--- pdns-recursor-3.6.1/pdns_recursor.cc	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/pdns_recursor.cc	2014-10-30 13:25:51.000000000 +0100
@@ -66,7 +66,7 @@
 #include "lua-recursor.hh"
 #include "version.hh"
 #include "responsestats.hh"
-
+#include "secpoll-recursor.hh"
 #ifndef RECURSOR
 #include "statbag.hh"
 StatBag S;
@@ -119,7 +119,8 @@
 int g_tcpTimeout;
 unsigned int g_maxMThreads;
 struct timeval g_now; // timestamp, updated (too) frequently
-map<int, ComboAddress> g_listenSocketsAddresses; // is shared across all threads right now
+typedef map<int, ComboAddress> listenSocketsAddresses_t; // is shared across all threads right now
+listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now
 
 __thread MT_t* MT; // the big MTasker
 
@@ -507,7 +508,8 @@
     if(getEDNSOpts(dc->d_mdp, &edo) && !dc->d_tcp) {
       maxanswersize = min(edo.d_packetsize, g_udpTruncationThreshold);
     }
-    
+    ComboAddress local;    
+    listenSocketsAddresses_t::const_iterator lociter;
     vector<DNSResourceRecord> ret;
     vector<uint8_t> packet;
 
@@ -548,9 +550,27 @@
     if(!dc->d_mdp.d_header.rd)
       sr.setCacheOnly();
 
-    // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
-    if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, g_listenSocketsAddresses[dc->d_socket], dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer)) {
-      res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
+    local.sin4.sin_family = dc->d_remote.sin4.sin_family;
+
+    lociter = g_listenSocketsAddresses.find(dc->d_socket);
+    if(lociter != g_listenSocketsAddresses.end()) {
+      local = lociter->second;
+    }
+    else {
+      socklen_t len = local.getSocklen();
+      getsockname(dc->d_socket, (sockaddr*)&local, &len); // if this fails, we're ok with it
+    }
+
+    // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve      
+    if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer)) {
+      try {
+        res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
+      }
+      catch(ImmediateServFailException &e) {
+        L<<Logger::Error<<"Sending SERVFAIL during resolve of '"<<dc->d_mdp.d_qname<<"' because: "<<e.reason<<endl;
+
+        res = RCode::ServFail;
+      }
 
       if(t_pdl->get()) {
         if(res == RCode::NoError) {
@@ -559,12 +579,12 @@
                   if(i->qtype.getCode() == dc->d_mdp.d_qtype && i->d_place == DNSResourceRecord::ANSWER)
                           break;
                 if(i == ret.end())
-                  (*t_pdl)->nodata(dc->d_remote, g_listenSocketsAddresses[dc->d_socket], dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
+                  (*t_pdl)->nodata(dc->d_remote,local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
               }
               else if(res == RCode::NXDomain)
-          (*t_pdl)->nxdomain(dc->d_remote, g_listenSocketsAddresses[dc->d_socket], dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
+          (*t_pdl)->nxdomain(dc->d_remote,local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
       
-      (*t_pdl)->postresolve(dc->d_remote, g_listenSocketsAddresses[dc->d_socket], dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
+      (*t_pdl)->postresolve(dc->d_remote,local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
       }
     }
     
@@ -1022,7 +1042,8 @@
     listen(fd, 128);
     deferredAdd.push_back(make_pair(fd, handleNewTCPQuestion));
     g_tcpListenSockets.push_back(fd);
-
+    // we don't need to update g_listenSocketsAddresses since it doesn't work for TCP/IP:
+    //  - fd is not that which we know here, but returned from accept()
     if(sin.sin4.sin_family == AF_INET) 
       L<<Logger::Error<<"Listening for TCP queries on "<< sin.toString() <<":"<<st.port<<endl;
     else
@@ -1161,7 +1182,7 @@
 static void houseKeeping(void *)
 try
 {
-  static __thread time_t last_stat, last_rootupdate, last_prune;
+  static __thread time_t last_stat, last_rootupdate, last_prune, last_secpoll;
   static __thread int cleanCounter=0;
   struct timeval now;
   Utility::gettimeofday(&now, 0);
@@ -1188,13 +1209,6 @@
     last_prune=time(0);
   }
   
-  if(!t_id) {
-    if(now.tv_sec - last_stat >= 1800) { 
-      doStats();
-      last_stat=time(0);
-    }
-  }
-  
   if(now.tv_sec - last_rootupdate > 7200) {
     SyncRes sr(now);
     sr.setDoEDNS0(true);
@@ -1209,13 +1223,23 @@
     else
       L<<Logger::Error<<"Failed to update . records, RCODE="<<res<<endl;
   }
+
+  if(!t_id) {
+    if(now.tv_sec - last_stat >= 1800) { 
+      doStats();
+      last_stat=time(0);
+    }
+
+    if(now.tv_sec - last_secpoll >= 3600) {
+      doSecPoll(&last_secpoll);
+    }
+  }
 }
 catch(PDNSException& ae)
 {
-  L<<Logger::Error<<"Fatal error: "<<ae.reason<<endl;
+  L<<Logger::Error<<"Fatal error in housekeeping thread: "<<ae.reason<<endl;
   throw;
 }
-;
 
 void makeThreadPipes()
 {
@@ -2099,6 +2123,7 @@
     ::arg().set("experimental-webserver-port", "Port of webserver to listen on") = "8082";
     ::arg().set("experimental-webserver-password", "Password required for accessing the webserver") = "";
     ::arg().set("experimental-api-config-dir", "Directory where REST API stores config and zones") = "";
+    ::arg().set("experimental-api-key", "REST API Static authentication key (required for API use)") = "";
     ::arg().set("carbon-ourname", "If set, overrides our reported hostname for carbon stats")="";
     ::arg().set("carbon-server", "If set, send metrics in carbon (graphite) format to this server")="";
     ::arg().set("carbon-interval", "Number of seconds between carbon (graphite) updates")="30";
@@ -2155,6 +2180,7 @@
     ::arg().set("minimum-ttl-override", "Set under adverse conditions, a minimum TTL")="0";
 
     ::arg().set("include-dir","Include *.conf files from this directory")="";
+    ::arg().set("security-poll-suffix","Domain name from which to query security update notifications")="secpoll.powerdns.com.";
 
     ::arg().setCmd("help","Provide a helpful message");
     ::arg().setCmd("version","Print version string");
diff -Nru pdns-recursor-3.6.1/powerdns-example-script.lua pdns-recursor-3.6.2/powerdns-example-script.lua
--- pdns-recursor-3.6.1/powerdns-example-script.lua	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/powerdns-example-script.lua	2014-10-30 13:25:52.000000000 +0100
@@ -5,7 +5,7 @@
 
 function preresolve ( remoteip, domain, qtype )
 
-	print ("prequery handler called for: ", remoteip, getlocaladdress(), domain, qtype)
+	print ("prequery handler called for: ", remoteip, "local: ", getlocaladdress(), domain, qtype)
 	pdnslog("a test message.. received query from "..remoteip.." on "..getlocaladdress(), pdns.loglevels.Info);
 
 	if endswith(domain, "f.f.7.7.b.1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa.")
diff -Nru pdns-recursor-3.6.1/rec_channel.hh pdns-recursor-3.6.2/rec_channel.hh
--- pdns-recursor-3.6.1/rec_channel.hh	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/rec_channel.hh	2014-10-30 13:25:51.000000000 +0100
@@ -4,6 +4,7 @@
 #include <map>
 #include <inttypes.h>
 #include <sys/un.h>
+#include <pthread.h>
 
 
 /** this class is used both to send and answer channel commands to the PowerDNS Recursor */
diff -Nru pdns-recursor-3.6.1/secpoll-recursor.cc pdns-recursor-3.6.2/secpoll-recursor.cc
--- pdns-recursor-3.6.1/secpoll-recursor.cc	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor-3.6.2/secpoll-recursor.cc	2014-10-30 13:25:52.000000000 +0100
@@ -0,0 +1,61 @@
+#include "secpoll-recursor.hh"
+#include "syncres.hh"
+#include "logger.hh"
+#include "arguments.hh"
+#include "version.hh"
+#include "version_generated.h"
+#include <stdint.h>
+#ifndef PACKAGEVERSION 
+#define PACKAGEVERSION PDNS_VERSION
+#endif
+
+uint32_t g_security_status;
+string g_security_message;
+
+void doSecPoll(time_t* last_secpoll)
+{
+  if(::arg()["security-poll-suffix"].empty())
+    return;
+
+  struct timeval now;
+  gettimeofday(&now, 0);
+  SyncRes sr(now);
+  
+  vector<DNSResourceRecord> ret;
+
+  string query = "recursor-" PACKAGEVERSION ".security-status."+::arg()["security-poll-suffix"];
+
+  if(*query.rbegin()!='.')
+    query+='.';
+
+  boost::replace_all(query, "+", "_");
+
+  int res=sr.beginResolve(query, QType(QType::TXT), 1, ret);
+  if(!res && !ret.empty()) {
+    string content=ret.begin()->content;
+    if(!content.empty() && content[0]=='"' && content[content.size()-1]=='"') {
+      content=content.substr(1, content.length()-2);
+    }
+      
+    pair<string, string> split = splitField(content, ' ');
+    
+    g_security_status = atoi(split.first.c_str());
+    g_security_message = split.second;
+
+    *last_secpoll=now.tv_sec;
+  }
+  else {
+    L<<Logger::Warning<<"Could not retrieve security status update for '" PACKAGEVERSION "' on '"+query+"', RCODE = "<< RCode::to_s(res)<<endl;
+    if(g_security_status == 1) // it was ok, not it is unknown
+      g_security_status = 0;
+    if(res == RCode::NXDomain) // if we had servfail, keep on trying more more frequently
+      *last_secpoll=now.tv_sec; 
+  }
+
+  if(g_security_status == 2) {
+    L<<Logger::Error<<"PowerDNS Security Update Recommended: "<<g_security_message<<endl;
+  }
+  else if(g_security_status == 3) {
+    L<<Logger::Error<<"PowerDNS Security Update Mandatory: "<<g_security_message<<endl;
+  }
+}
diff -Nru pdns-recursor-3.6.1/secpoll-recursor.hh pdns-recursor-3.6.2/secpoll-recursor.hh
--- pdns-recursor-3.6.1/secpoll-recursor.hh	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor-3.6.2/secpoll-recursor.hh	2014-10-30 13:25:51.000000000 +0100
@@ -0,0 +1,11 @@
+#ifndef PDNS_SECPOLL_RECURSOR_HH
+#define PDNS_SECPOLL_RECURSOR_HH
+#include <time.h>
+#include "namespaces.hh"
+#include <stdint.h>
+
+void doSecPoll(time_t* );
+extern uint32_t g_security_status;
+extern std::string g_security_message;
+
+#endif
diff -Nru pdns-recursor-3.6.1/syncres.cc pdns-recursor-3.6.2/syncres.cc
--- pdns-recursor-3.6.1/syncres.cc	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/syncres.cc	2014-10-30 13:25:51.000000000 +0100
@@ -923,6 +923,7 @@
           }
           else {
             s_outqueries++; d_outqueries++;
+            if(d_outqueries > 50) throw ImmediateServFailException("more than 50 queries sent while resolving "+qname);
           TryTCP:
             if(doTCP) {
               LOG(prefix<<qname<<": using TCP with "<< remoteIP->toStringWithPort() <<endl);
diff -Nru pdns-recursor-3.6.1/syncres.hh pdns-recursor-3.6.2/syncres.hh
--- pdns-recursor-3.6.1/syncres.hh	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/syncres.hh	2014-10-30 13:25:51.000000000 +0100
@@ -593,6 +593,13 @@
   static AtomicCounter s_currentConnections; //!< total number of current TCP connections
 };
 
+class ImmediateServFailException
+{
+public:
+  ImmediateServFailException(string r){reason=r;};
+
+  string reason; //! Print this to tell the user what went wrong
+};
 
 struct RemoteKeeper
 {
diff -Nru pdns-recursor-3.6.1/webserver.cc pdns-recursor-3.6.2/webserver.cc
--- pdns-recursor-3.6.1/webserver.cc	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/webserver.cc	2014-10-30 13:25:52.000000000 +0100
@@ -20,6 +20,7 @@
     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
 #include "utility.hh"
+#include "arguments.hh"
 #include "webserver.hh"
 #include "misc.hh"
 #include <vector>
@@ -28,6 +29,7 @@
 #include "dns.hh"
 #include "base64.hh"
 #include "json.hh"
+#include <yahttp/router.hpp>
 
 struct connectionThreadData {
   WebServer* webServer;
@@ -36,11 +38,47 @@
 
 void HttpRequest::json(rapidjson::Document& document)
 {
+  if(this->body.empty()) {
+    L<<Logger::Debug<<"HTTP: JSON document expected in request body, but body was empty" << endl;
+    throw HttpBadRequestException();
+  }
   if(document.Parse<0>(this->body.c_str()).HasParseError()) {
+    L<<Logger::Debug<<"HTTP: parsing of JSON document failed" << endl;
     throw HttpBadRequestException();
   }
 }
 
+bool HttpRequest::compareAuthorization(const string &expected_password)
+{
+  // validate password
+  YaHTTP::strstr_map_t::iterator header = headers.find("authorization");
+  bool auth_ok = false;
+  if (header != headers.end() && toLower(header->second).find("basic ") == 0) {
+    string cookie = header->second.substr(6);
+
+    string plain;
+    B64Decode(cookie, plain);
+
+    vector<string> cparts;
+    stringtok(cparts, plain, ":");
+
+    // this gets rid of terminating zeros
+    auth_ok = (cparts.size()==2 && (0==strcmp(cparts[1].c_str(), expected_password.c_str())));
+  }
+  return auth_ok;
+}
+
+bool HttpRequest::compareHeader(const string &header_name, const string &expected_value)
+{
+  YaHTTP::strstr_map_t::iterator header = headers.find(header_name);
+  if (header == headers.end())
+    return false;
+
+  // this gets rid of terminating zeros
+  return (0==strcmp(header->second.c_str(), expected_value.c_str()));
+}
+
+
 void HttpResponse::setBody(rapidjson::Document& document)
 {
   this->body = makeStringFromDocument(document);
@@ -51,63 +89,44 @@
   return ::B64Decode(strInput, strOutput);
 }
 
-// url is supposed to start with a slash.
-// url can contain variable names, marked as <variable>; such variables
-// are parsed out during routing and are put into the "pathArgs" map.
-// route() makes no assumptions about the contents of variables except
-// that the following URL segment can't be part of the variable.
-//
-// Note: ORDER of registration MATTERS:
-// URLs that do a more specific match should come FIRST.
-//
-// Examples:
-//   registerHandler("/foo/<bar>/<baz>", &foobarbaz);
-//   registerHandler("/foo/<bar>", &foobar);
-//   registerHandler("/foo", &foo);
-//   registerHandler("/", &index);
-void WebServer::registerHandler(const string& url, HandlerFunction handler)
+static void bareHandlerWrapper(WebServer::HandlerFunction handler, YaHTTP::Request* req, YaHTTP::Response* resp)
 {
-  std::size_t pos = 0, lastpos = 0;
-
-  HandlerRegistration reg;
-  while ((pos = url.find('<', lastpos)) != std::string::npos) {
-    std::string part = url.substr(lastpos, pos-lastpos);
-    lastpos = pos;
-    pos = url.find('>', pos);
-
-    if (pos == std::string::npos) {
-      throw std::logic_error("invalid url given");
-    }
+  // wrapper to convert from YaHTTP::* to our subclasses
+  handler(static_cast<HttpRequest*>(req), static_cast<HttpResponse*>(resp));
+}
 
-    std::string paramName = url.substr(lastpos+1, pos-lastpos-1);
-    lastpos = pos+1;
+void WebServer::registerBareHandler(const string& url, HandlerFunction handler)
+{
+  YaHTTP::THandlerFunction f = boost::bind(&bareHandlerWrapper, handler, _1, _2);
+  YaHTTP::Router::Any(url, f);
+}
 
-    reg.urlParts.push_back(part);
-    reg.paramNames.push_back(paramName);
-  }
-  std::string remainder = url.substr(lastpos);
-  if (!remainder.empty()) {
-    reg.urlParts.push_back(remainder);
-    reg.paramNames.push_back("");
+static void apiWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp) {
+  const string& api_key = ::arg()["experimental-api-key"];
+  if (api_key.empty()) {
+    L<<Logger::Debug<<"HTTP API Request \"" << req->url.path << "\": Authentication failed, API Key missing in config" << endl;
+    throw HttpUnauthorizedException();
+  }
+  bool auth_ok = req->compareHeader("x-api-key", api_key);
+  if (!auth_ok) {
+    L<<Logger::Debug<<"HTTP Request \"" << req->url.path << "\": Authentication by API Key failed" << endl;
+    throw HttpUnauthorizedException();
   }
-  reg.handler = handler;
-  d_handlers.push_back(reg);
-}
 
-static void apiWrapper(boost::function<void(HttpRequest*,HttpResponse*)> handler, HttpRequest* req, HttpResponse* resp) {
   resp->headers["Access-Control-Allow-Origin"] = "*";
   resp->headers["Content-Type"] = "application/json";
 
   string callback;
 
-  if(req->parameters.count("callback")) {
-    callback=req->parameters["callback"];
-    req->parameters.erase("callback");
+  if(req->getvars.count("callback")) {
+    callback=req->getvars["callback"];
+    req->getvars.erase("callback");
   }
 
-  req->parameters.erase("_"); // jQuery cache buster
+  req->getvars.erase("_"); // jQuery cache buster
 
   try {
+    resp->status = 200;
     handler(req, resp);
   } catch (ApiException &e) {
     resp->body = returnJsonError(e.what());
@@ -131,47 +150,25 @@
 
 void WebServer::registerApiHandler(const string& url, HandlerFunction handler) {
   HandlerFunction f = boost::bind(&apiWrapper, handler, _1, _2);
-  registerHandler(url, f);
+  registerBareHandler(url, f);
 }
 
-bool WebServer::route(const std::string& url, std::map<std::string, std::string>& pathArgs, HandlerFunction** handler)
-{
-  for (std::list<HandlerRegistration>::iterator reg=d_handlers.begin(); reg != d_handlers.end(); ++reg) {
-    bool matches = true;
-    size_t lastpos = 0, pos = 0;
-    string lastParam;
-    pathArgs.clear();
-    for (std::list<string>::iterator urlPart = reg->urlParts.begin(), param = reg->paramNames.begin();
-         urlPart != reg->urlParts.end() && param != reg->paramNames.end();
-         urlPart++, param++) {
-      if (!urlPart->empty()) {
-        pos = url.find(*urlPart, lastpos);
-        if (pos == std::string::npos) {
-          matches = false;
-          break;
-        }
-        if (!lastParam.empty()) {
-          // store
-          pathArgs[lastParam] = url.substr(lastpos, pos-lastpos);
-        }
-        lastpos = pos + urlPart->size();
-        lastParam = *param;
-      }
-    }
-    if (matches) {
-      if (!lastParam.empty()) {
-        // store trailing parameter
-        pathArgs[lastParam] = url.substr(lastpos, pos-lastpos);
-      } else if (lastpos != url.size()) {
-        matches = false;
-        continue;
-      }
-
-      *handler = &reg->handler;
-      return true;
+static void webWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp) {
+  const string& web_password = arg()["webserver-password"];
+  if (!web_password.empty()) {
+    bool auth_ok = req->compareAuthorization(web_password);
+    if (!auth_ok) {
+      L<<Logger::Debug<<"HTTP Request \"" << req->url.path << "\": Web Authentication failed" << endl;
+      throw HttpUnauthorizedException();
     }
   }
-  return false;
+
+  handler(req, resp);
+}
+
+void WebServer::registerWebHandler(const string& url, HandlerFunction handler) {
+  HandlerFunction f = boost::bind(&webWrapper, handler, _1, _2);
+  registerBareHandler(url, f);
 }
 
 static void *WebServerConnectionThreadStart(void *p) {
@@ -187,13 +184,14 @@
 
 HttpResponse WebServer::handleRequest(HttpRequest req)
 {
-  HttpResponse resp(req);
+  HttpResponse resp;
 
   // set default headers
   resp.headers["Content-Type"] = "text/html; charset=utf-8";
 
   try {
     if (!req.complete) {
+      L<<Logger::Debug<<"HTTP: Incomplete request" << endl;
       throw HttpBadRequestException();
     }
 
@@ -210,38 +208,19 @@
       }
     }
 
-    if (!d_password.empty()) {
-      // validate password
-      header = req.headers.find("authorization");
-      bool auth_ok = false;
-      if (header != req.headers.end() && toLower(header->second).find("basic ") == 0) {
-        string cookie = header->second.substr(6);
-
-        string plain;
-        B64Decode(cookie, plain);
-
-        vector<string> cparts;
-        stringtok(cparts, plain, ":");
-
-        // this gets rid of terminating zeros
-        auth_ok = (cparts.size()==2 && (0==strcmp(cparts[1].c_str(), d_password.c_str())));
-      }
-      if (!auth_ok) {
-        L<<Logger::Debug<<"HTTP Request \"" << req.url.path << "\": Authentication failed" << endl;
-        throw HttpUnauthorizedException();
-      }
-    }
-
-    HandlerFunction *handler;
-    if (!route(req.url.path, req.path_parameters, &handler)) {
+    YaHTTP::THandlerFunction handler;
+    if (!YaHTTP::Router::Route(&req, handler)) {
       L<<Logger::Debug<<"HTTP: No route found for \"" << req.url.path << "\"" << endl;
       throw HttpNotFoundException();
     }
 
     try {
-      (*handler)(&req, &resp);
+      handler(&req, &resp);
       L<<Logger::Debug<<"HTTP: Result for \"" << req.url.path << "\": " << resp.status << ", body length: " << resp.body.size() << endl;
     }
+    catch(HttpException) {
+      throw;
+    }
     catch(PDNSException &e) {
       L<<Logger::Error<<"HTTP ISE for \""<< req.url.path << "\": Exception: " << e.reason << endl;
       throw HttpInternalServerErrorException();
@@ -281,7 +260,8 @@
 void WebServer::serveConnection(Socket *client)
 try {
   HttpRequest req;
-  YaHTTP::AsyncRequestLoader yarl(&req);
+  YaHTTP::AsyncRequestLoader yarl;
+  yarl.initialize(&req);
   int timeout = 5;
   client->setNonBlocking();
 
@@ -298,6 +278,7 @@
         break;
       }
     }
+    yarl.finalize();
   } catch (YaHTTP::ParseError &e) {
     // request stays incomplete
   }
@@ -319,11 +300,10 @@
   L<<Logger::Error<<"HTTP: Unknown exception"<<endl;
 }
 
-WebServer::WebServer(const string &listenaddress, int port, const string &password) : d_server(NULL)
+WebServer::WebServer(const string &listenaddress, int port) : d_server(NULL)
 {
   d_listenaddress=listenaddress;
   d_port=port;
-  d_password=password;
 }
 
 void WebServer::bind()
diff -Nru pdns-recursor-3.6.1/webserver.hh pdns-recursor-3.6.2/webserver.hh
--- pdns-recursor-3.6.1/webserver.hh	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/webserver.hh	2014-10-30 13:25:51.000000000 +0100
@@ -32,21 +32,25 @@
 #include "namespaces.hh"
 #include "sstuff.hh"
 
+class WebServer;
+
 class HttpRequest : public YaHTTP::Request {
 public:
   HttpRequest() : YaHTTP::Request(), accept_json(false), accept_html(false), complete(false) { };
 
-  map<string,string> path_parameters;
   bool accept_json;
   bool accept_html;
   bool complete;
   void json(rapidjson::Document& document);
+
+  // checks password _only_.
+  bool compareAuthorization(const string &expected_password);
+  bool compareHeader(const string &header_name, const string &expected_value);
 };
 
 class HttpResponse: public YaHTTP::Response {
 public:
   HttpResponse() : YaHTTP::Response() { };
-  HttpResponse(const YaHTTP::Request &req) : YaHTTP::Response(req) { };
   HttpResponse(const YaHTTP::Response &resp) : YaHTTP::Response(resp) { };
 
   void setBody(rapidjson::Document& document);
@@ -127,7 +131,7 @@
 class WebServer : public boost::noncopyable
 {
 public:
-  WebServer(const string &listenaddress, int port, const string &password="");
+  WebServer(const string &listenaddress, int port);
   void bind();
   void go();
 
@@ -135,19 +139,13 @@
   HttpResponse handleRequest(HttpRequest request);
 
   typedef boost::function<void(HttpRequest* req, HttpResponse* resp)> HandlerFunction;
-  struct HandlerRegistration {
-    std::list<string> urlParts;
-    std::list<string> paramNames;
-    HandlerFunction handler;
-  };
-
-  void registerHandler(const string& url, HandlerFunction handler);
   void registerApiHandler(const string& url, HandlerFunction handler);
+  void registerWebHandler(const string& url, HandlerFunction handler);
 
 protected:
   static char B64Decode1(char cInChar);
   static int B64Decode(const std::string& strInput, std::string& strOutput);
-  bool route(const std::string& url, std::map<std::string, std::string>& urlArgs, HandlerFunction** handler);
+  void registerBareHandler(const string& url, HandlerFunction handler);
 
   virtual Server* createServer() {
     return new Server(d_listenaddress, d_port);
@@ -155,7 +153,6 @@
 
   string d_listenaddress;
   int d_port;
-  std::list<HandlerRegistration> d_handlers;
   string d_password;
   Server* d_server;
 };
diff -Nru pdns-recursor-3.6.1/ws-api.cc pdns-recursor-3.6.2/ws-api.cc
--- pdns-recursor-3.6.1/ws-api.cc	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/ws-api.cc	2014-10-30 13:25:52.000000000 +0100
@@ -188,7 +188,7 @@
     throw HttpMethodNotAllowedException();
 
   string prefix = " " + s_programname + "[";
-  resp->body = logGrep(req->parameters["q"], ::arg()["experimental-logfile"], prefix);
+  resp->body = logGrep(req->getvars["q"], ::arg()["experimental-logfile"], prefix);
 }
 
 void apiServerStatistics(HttpRequest* req, HttpResponse* resp) {
diff -Nru pdns-recursor-3.6.1/ws-recursor.cc pdns-recursor-3.6.2/ws-recursor.cc
--- pdns-recursor-3.6.1/ws-recursor.cc	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/ws-recursor.cc	2014-10-30 13:25:52.000000000 +0100
@@ -331,7 +331,7 @@
 
 static void apiServerZoneDetail(HttpRequest* req, HttpResponse* resp)
 {
-  string zonename = apiZoneIdToName(req->path_parameters["id"]);
+  string zonename = apiZoneIdToName(req->parameters["id"]);
   zonename += ".";
 
   SyncRes::domainmap_t::const_iterator iter = t_sstorage->domainmap->find(zonename);
@@ -367,7 +367,7 @@
   if(req->method != "GET")
     throw HttpMethodNotAllowedException();
 
-  string q = req->parameters["q"];
+  string q = req->getvars["q"];
   if (q.empty())
     throw ApiException("Query q can't be blank");
 
@@ -420,7 +420,7 @@
 {
   RecursorControlParser rcp; // inits
 
-  d_ws = new AsyncWebServer(fdm, arg()["experimental-webserver-address"], arg().asNum("experimental-webserver-port"), arg()["experimental-webserver-password"]);
+  d_ws = new AsyncWebServer(fdm, arg()["experimental-webserver-address"], arg().asNum("experimental-webserver-port"));
   d_ws->bind();
 
   // legacy dispatch
@@ -442,9 +442,9 @@
 {
   string command;
 
-  if(req->parameters.count("command")) {
-    command = req->parameters["command"];
-    req->parameters.erase("command");
+  if(req->getvars.count("command")) {
+    command = req->getvars["command"];
+    req->getvars.erase("command");
   }
 
   map<string, string> stats; 
@@ -476,7 +476,7 @@
     return;
   }
   else if(command == "zone") {
-    string arg_zone = req->parameters["zone"];
+    string arg_zone = req->getvars["zone"];
     SyncRes::domainmap_t::const_iterator ret = t_sstorage->domainmap->find(arg_zone);
     if (ret != t_sstorage->domainmap->end()) {
       Document doc;
@@ -525,7 +525,7 @@
     }
   }
   else if(command == "flush-cache") {
-    string canon=toCanonic("", req->parameters["domain"]);
+    string canon=toCanonic("", req->getvars["domain"]);
     int count = broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, canon));
     count+=broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, canon));
     stats["number"]=lexical_cast<string>(count);
@@ -542,7 +542,7 @@
   }
   else if(command == "log-grep") {
     // legacy parameter name hack
-    req->parameters["q"] = req->parameters["needle"];
+    req->getvars["q"] = req->getvars["needle"];
     apiServerSearchLog(req, resp);
     return;
   }
@@ -584,7 +584,8 @@
 void AsyncWebServer::serveConnection(Socket *client)
 {
   HttpRequest req;
-  YaHTTP::AsyncRequestLoader yarl(&req);
+  YaHTTP::AsyncRequestLoader yarl;
+  yarl.initialize(&req);
   client->setNonBlocking();
 
   string data;
@@ -599,6 +600,7 @@
         break;
       }
     }
+    yarl.finalize();
   } catch (YaHTTP::ParseError &e) {
     // request stays incomplete
   }
diff -Nru pdns-recursor-3.6.1/ws-recursor.hh pdns-recursor-3.6.2/ws-recursor.hh
--- pdns-recursor-3.6.1/ws-recursor.hh	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor-3.6.2/ws-recursor.hh	2014-10-30 13:25:51.000000000 +0100
@@ -45,8 +45,8 @@
 class AsyncWebServer : public WebServer
 {
 public:
-  AsyncWebServer(FDMultiplexer* fdm, const string &listenaddress, int port, const string &password="") :
-    WebServer(listenaddress, port, password), d_fdm(fdm) { };
+  AsyncWebServer(FDMultiplexer* fdm, const string &listenaddress, int port) :
+    WebServer(listenaddress, port), d_fdm(fdm) { };
   void go();
 
 private:
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/build-scripts/redhat/pdns-recursor-test.spec pdns-recursor/build-scripts/redhat/pdns-recursor-test.spec
--- pdns-recursor-3.6.1/build-scripts/redhat/pdns-recursor-test.spec	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor/build-scripts/redhat/pdns-recursor-test.spec	2014-10-30 14:16:42.481477759 +0100
@@ -9,7 +9,7 @@
 Epoch:			0
 Group:			System Environment/Daemons
 License:		GPL
-Source:			http://downloads.powerdns.com/releases/%{name}-3.6.1.tar.bz2
+Source:			http://downloads.powerdns.com/releases/%{name}-3.6.2.tar.bz2
 
 BuildRequires:		boost-devel >= 1.39.0
 BuildRequires:		lua-devel >= 5.2
@@ -20,7 +20,7 @@
 PowerDNS recursor
 
 %prep
-%setup -q -n %{name}-3.6.1
+%setup -q -n %{name}-3.6.2
 
 %build
 %configure
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/config.h pdns-recursor/config.h
--- pdns-recursor-3.6.1/config.h	2014-10-30 14:20:02.000000000 +0100
+++ pdns-recursor/config.h	2014-10-30 14:16:42.481477759 +0100
@@ -1,4 +1,4 @@
 #define RECURSOR
-#define VERSION "3.6.1-apikey"
-#define DIST_HOST "ch@build.deduktiva.com"
+#define VERSION "3.6.2"
+#define DIST_HOST "jenkins@autotest.powerdns.com"
 #define HAVE_BOOST 1
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/debian/changelog pdns-recursor/debian/changelog
--- pdns-recursor-3.6.1/debian/changelog	2014-10-21 21:31:43.000000000 +0200
+++ pdns-recursor/debian/changelog	2014-10-30 17:34:48.026710773 +0100
@@ -1,3 +1,10 @@
+pdns-recursor (3.6.2-1) unstable; urgency=high
+
+  * Imported Upstream version 3.6.2, a bugfix release (Closes: #767368)
+  * Remove API key patch, which has been incorporated upstream.
+
+ -- Christian Hofstaedtler <zeha@debian.org>  Thu, 30 Oct 2014 17:22:19 +0100
+
 pdns-recursor (3.6.1-3) unstable; urgency=medium
 
   * Apply API key patch from upstream
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/debian/patches/series pdns-recursor/debian/patches/series
--- pdns-recursor-3.6.1/debian/patches/series	2014-10-21 20:52:44.000000000 +0200
+++ pdns-recursor/debian/patches/series	2014-10-30 14:17:30.640660791 +0100
@@ -1 +0,0 @@
-apikey-3-6-2.patch
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/Makefile pdns-recursor/Makefile
--- pdns-recursor-3.6.1/Makefile	2014-10-30 14:20:02.000000000 +0100
+++ pdns-recursor/Makefile	2014-10-30 14:16:42.481477759 +0100
@@ -24,7 +24,7 @@
 lua-pdns.o lua-recursor.o randomhelper.o recpacketcache.o dns.o \
 reczones.o base32.o nsecrecords.o json.o ws-recursor.o ws-api.o \
 version.o responsestats.o webserver.o ext/yahttp/yahttp/reqresp.o ext/yahttp/yahttp/router.o \
-rec-carbon.o
+rec-carbon.o secpoll-recursor.o
 
 REC_CONTROL_OBJECTS=rec_channel.o rec_control.o arguments.o misc.o \
 	unix_utility.o logger.o qtype.o
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/pdns_recursor.cc pdns-recursor/pdns_recursor.cc
--- pdns-recursor-3.6.1/pdns_recursor.cc	2014-10-30 14:20:02.000000000 +0100
+++ pdns-recursor/pdns_recursor.cc	2014-10-30 14:16:42.485477358 +0100
@@ -66,7 +66,7 @@
 #include "lua-recursor.hh"
 #include "version.hh"
 #include "responsestats.hh"
-
+#include "secpoll-recursor.hh"
 #ifndef RECURSOR
 #include "statbag.hh"
 StatBag S;
@@ -119,7 +119,8 @@
 int g_tcpTimeout;
 unsigned int g_maxMThreads;
 struct timeval g_now; // timestamp, updated (too) frequently
-map<int, ComboAddress> g_listenSocketsAddresses; // is shared across all threads right now
+typedef map<int, ComboAddress> listenSocketsAddresses_t; // is shared across all threads right now
+listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now
 
 __thread MT_t* MT; // the big MTasker
 
@@ -507,7 +508,8 @@
     if(getEDNSOpts(dc->d_mdp, &edo) && !dc->d_tcp) {
       maxanswersize = min(edo.d_packetsize, g_udpTruncationThreshold);
     }
-    
+    ComboAddress local;    
+    listenSocketsAddresses_t::const_iterator lociter;
     vector<DNSResourceRecord> ret;
     vector<uint8_t> packet;
 
@@ -548,9 +550,27 @@
     if(!dc->d_mdp.d_header.rd)
       sr.setCacheOnly();
 
-    // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
-    if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, g_listenSocketsAddresses[dc->d_socket], dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer)) {
-      res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
+    local.sin4.sin_family = dc->d_remote.sin4.sin_family;
+
+    lociter = g_listenSocketsAddresses.find(dc->d_socket);
+    if(lociter != g_listenSocketsAddresses.end()) {
+      local = lociter->second;
+    }
+    else {
+      socklen_t len = local.getSocklen();
+      getsockname(dc->d_socket, (sockaddr*)&local, &len); // if this fails, we're ok with it
+    }
+
+    // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve      
+    if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer)) {
+      try {
+        res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
+      }
+      catch(ImmediateServFailException &e) {
+        L<<Logger::Error<<"Sending SERVFAIL during resolve of '"<<dc->d_mdp.d_qname<<"' because: "<<e.reason<<endl;
+
+        res = RCode::ServFail;
+      }
 
       if(t_pdl->get()) {
         if(res == RCode::NoError) {
@@ -559,12 +579,12 @@
                   if(i->qtype.getCode() == dc->d_mdp.d_qtype && i->d_place == DNSResourceRecord::ANSWER)
                           break;
                 if(i == ret.end())
-                  (*t_pdl)->nodata(dc->d_remote, g_listenSocketsAddresses[dc->d_socket], dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
+                  (*t_pdl)->nodata(dc->d_remote,local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
               }
               else if(res == RCode::NXDomain)
-          (*t_pdl)->nxdomain(dc->d_remote, g_listenSocketsAddresses[dc->d_socket], dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
+          (*t_pdl)->nxdomain(dc->d_remote,local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
       
-      (*t_pdl)->postresolve(dc->d_remote, g_listenSocketsAddresses[dc->d_socket], dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
+      (*t_pdl)->postresolve(dc->d_remote,local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
       }
     }
     
@@ -1022,7 +1042,8 @@
     listen(fd, 128);
     deferredAdd.push_back(make_pair(fd, handleNewTCPQuestion));
     g_tcpListenSockets.push_back(fd);
-
+    // we don't need to update g_listenSocketsAddresses since it doesn't work for TCP/IP:
+    //  - fd is not that which we know here, but returned from accept()
     if(sin.sin4.sin_family == AF_INET) 
       L<<Logger::Error<<"Listening for TCP queries on "<< sin.toString() <<":"<<st.port<<endl;
     else
@@ -1161,7 +1182,7 @@
 static void houseKeeping(void *)
 try
 {
-  static __thread time_t last_stat, last_rootupdate, last_prune;
+  static __thread time_t last_stat, last_rootupdate, last_prune, last_secpoll;
   static __thread int cleanCounter=0;
   struct timeval now;
   Utility::gettimeofday(&now, 0);
@@ -1188,13 +1209,6 @@
     last_prune=time(0);
   }
   
-  if(!t_id) {
-    if(now.tv_sec - last_stat >= 1800) { 
-      doStats();
-      last_stat=time(0);
-    }
-  }
-  
   if(now.tv_sec - last_rootupdate > 7200) {
     SyncRes sr(now);
     sr.setDoEDNS0(true);
@@ -1209,13 +1223,23 @@
     else
       L<<Logger::Error<<"Failed to update . records, RCODE="<<res<<endl;
   }
+
+  if(!t_id) {
+    if(now.tv_sec - last_stat >= 1800) { 
+      doStats();
+      last_stat=time(0);
+    }
+
+    if(now.tv_sec - last_secpoll >= 3600) {
+      doSecPoll(&last_secpoll);
+    }
+  }
 }
 catch(PDNSException& ae)
 {
-  L<<Logger::Error<<"Fatal error: "<<ae.reason<<endl;
+  L<<Logger::Error<<"Fatal error in housekeeping thread: "<<ae.reason<<endl;
   throw;
 }
-;
 
 void makeThreadPipes()
 {
@@ -2156,6 +2180,7 @@
     ::arg().set("minimum-ttl-override", "Set under adverse conditions, a minimum TTL")="0";
 
     ::arg().set("include-dir","Include *.conf files from this directory")="";
+    ::arg().set("security-poll-suffix","Domain name from which to query security update notifications")="secpoll.powerdns.com.";
 
     ::arg().setCmd("help","Provide a helpful message");
     ::arg().setCmd("version","Print version string");
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/powerdns-example-script.lua pdns-recursor/powerdns-example-script.lua
--- pdns-recursor-3.6.1/powerdns-example-script.lua	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor/powerdns-example-script.lua	2014-10-30 14:16:42.485477358 +0100
@@ -5,7 +5,7 @@
 
 function preresolve ( remoteip, domain, qtype )
 
-	print ("prequery handler called for: ", remoteip, getlocaladdress(), domain, qtype)
+	print ("prequery handler called for: ", remoteip, "local: ", getlocaladdress(), domain, qtype)
 	pdnslog("a test message.. received query from "..remoteip.." on "..getlocaladdress(), pdns.loglevels.Info);
 
 	if endswith(domain, "f.f.7.7.b.1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa.")
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/rec_channel.hh pdns-recursor/rec_channel.hh
--- pdns-recursor-3.6.1/rec_channel.hh	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor/rec_channel.hh	2014-10-30 14:16:42.485477358 +0100
@@ -4,6 +4,7 @@
 #include <map>
 #include <inttypes.h>
 #include <sys/un.h>
+#include <pthread.h>
 
 
 /** this class is used both to send and answer channel commands to the PowerDNS Recursor */
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/secpoll-recursor.cc pdns-recursor/secpoll-recursor.cc
--- pdns-recursor-3.6.1/secpoll-recursor.cc	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor/secpoll-recursor.cc	2014-10-30 14:16:42.485477358 +0100
@@ -0,0 +1,61 @@
+#include "secpoll-recursor.hh"
+#include "syncres.hh"
+#include "logger.hh"
+#include "arguments.hh"
+#include "version.hh"
+#include "version_generated.h"
+#include <stdint.h>
+#ifndef PACKAGEVERSION 
+#define PACKAGEVERSION PDNS_VERSION
+#endif
+
+uint32_t g_security_status;
+string g_security_message;
+
+void doSecPoll(time_t* last_secpoll)
+{
+  if(::arg()["security-poll-suffix"].empty())
+    return;
+
+  struct timeval now;
+  gettimeofday(&now, 0);
+  SyncRes sr(now);
+  
+  vector<DNSResourceRecord> ret;
+
+  string query = "recursor-" PACKAGEVERSION ".security-status."+::arg()["security-poll-suffix"];
+
+  if(*query.rbegin()!='.')
+    query+='.';
+
+  boost::replace_all(query, "+", "_");
+
+  int res=sr.beginResolve(query, QType(QType::TXT), 1, ret);
+  if(!res && !ret.empty()) {
+    string content=ret.begin()->content;
+    if(!content.empty() && content[0]=='"' && content[content.size()-1]=='"') {
+      content=content.substr(1, content.length()-2);
+    }
+      
+    pair<string, string> split = splitField(content, ' ');
+    
+    g_security_status = atoi(split.first.c_str());
+    g_security_message = split.second;
+
+    *last_secpoll=now.tv_sec;
+  }
+  else {
+    L<<Logger::Warning<<"Could not retrieve security status update for '" PACKAGEVERSION "' on '"+query+"', RCODE = "<< RCode::to_s(res)<<endl;
+    if(g_security_status == 1) // it was ok, not it is unknown
+      g_security_status = 0;
+    if(res == RCode::NXDomain) // if we had servfail, keep on trying more more frequently
+      *last_secpoll=now.tv_sec; 
+  }
+
+  if(g_security_status == 2) {
+    L<<Logger::Error<<"PowerDNS Security Update Recommended: "<<g_security_message<<endl;
+  }
+  else if(g_security_status == 3) {
+    L<<Logger::Error<<"PowerDNS Security Update Mandatory: "<<g_security_message<<endl;
+  }
+}
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/secpoll-recursor.hh pdns-recursor/secpoll-recursor.hh
--- pdns-recursor-3.6.1/secpoll-recursor.hh	1970-01-01 01:00:00.000000000 +0100
+++ pdns-recursor/secpoll-recursor.hh	2014-10-30 14:16:42.485477358 +0100
@@ -0,0 +1,11 @@
+#ifndef PDNS_SECPOLL_RECURSOR_HH
+#define PDNS_SECPOLL_RECURSOR_HH
+#include <time.h>
+#include "namespaces.hh"
+#include <stdint.h>
+
+void doSecPoll(time_t* );
+extern uint32_t g_security_status;
+extern std::string g_security_message;
+
+#endif
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/syncres.cc pdns-recursor/syncres.cc
--- pdns-recursor-3.6.1/syncres.cc	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor/syncres.cc	2014-10-30 14:16:42.485477358 +0100
@@ -923,6 +923,7 @@
           }
           else {
             s_outqueries++; d_outqueries++;
+            if(d_outqueries > 50) throw ImmediateServFailException("more than 50 queries sent while resolving "+qname);
           TryTCP:
             if(doTCP) {
               LOG(prefix<<qname<<": using TCP with "<< remoteIP->toStringWithPort() <<endl);
diff -Nru '--exclude=.pc' '--exclude=.git' '--exclude=debian/patches/apikey-3-6-2.patch' pdns-recursor-3.6.1/syncres.hh pdns-recursor/syncres.hh
--- pdns-recursor-3.6.1/syncres.hh	2014-09-09 11:33:33.000000000 +0200
+++ pdns-recursor/syncres.hh	2014-10-30 14:16:42.485477358 +0100
@@ -593,6 +593,13 @@
   static AtomicCounter s_currentConnections; //!< total number of current TCP connections
 };
 
+class ImmediateServFailException
+{
+public:
+  ImmediateServFailException(string r){reason=r;};
+
+  string reason; //! Print this to tell the user what went wrong
+};
 
 struct RemoteKeeper
 {

--- End Message ---
--- Begin Message ---
On Thu, Oct 30, 2014 at 06:04:18PM +0100, Christian Hofstaedtler wrote:
> Please unblock package pdns-recursor; while the new version is a new
> upstream release, it's a bug-fix only release, which fixes at least one
> bug in Debian, and a few other ones.
> It also incorporates a patch that I carried in the previous version,
> thereby reducing the delta between Debian and upstream back to 0.

Unblocked but not aged.

Thanks,

-- 
Jonathan Wiltshire                                      jmw@debian.org
Debian Developer                         http://people.debian.org/~jmw

4096R: 0xD3524C51 / 0A55 B7C5 1223 3942 86EC  74C3 5394 479D D352 4C51

Attachment: signature.asc
Description: Digital signature


--- End Message ---

Reply to: