Version in base suite: 1.9.10-1+deb13u1 Base version: dnsdist_1.9.10-1+deb13u1 Target version: dnsdist_1.9.14-0+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/d/dnsdist/dnsdist_1.9.10-1+deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/d/dnsdist/dnsdist_1.9.14-0+deb13u1.dsc Makefile.am | 2 Makefile.in | 2 configure | 20 configure.ac | 2 debian/changelog | 12 debian/gbp.conf | 1 debian/patches/series | 1 debian/patches/upstream/CVE-2025-8671-CVE-2025-30187-1.9.10.patch | 374 -- debian/upstream/signing-key.asc | 143 - debian/watch | 2 dnscrypt.cc | 7 dnsdist-backend.cc | 8 dnsdist-console.cc | 1 dnsdist-crypto.cc | 4 dnsdist-discovery.cc | 2 dnsdist-doh-common.hh | 3 dnsdist-dynblocks.hh | 1 dnsdist-lua-actions.cc | 30 dnsdist-lua-bindings-network.cc | 6 dnsdist-lua-ffi-interface.h | 2 dnsdist-lua-ffi-interface.inc | 2 dnsdist-lua-ffi.cc | 91 dnsdist-lua-inspection-ffi.cc | 8 dnsdist-lua-inspection.cc | 2 dnsdist-lua.cc | 21 dnsdist-lua.hh | 2 dnsdist-nghttp2-in.cc | 149 - dnsdist-nghttp2-in.hh | 3 dnsdist-nghttp2.cc | 59 dnsdist-tcp-downstream.cc | 12 dnsdist-tcp-downstream.hh | 7 dnsdist-tcp.cc | 22 dnsdist-tcp.hh | 23 dnsdist-web.cc | 16 dnsdist-web.hh | 1 dnsdist.1 | 2 dnsdist.cc | 6 dnsdist.hh | 1 dnsdist.service.in | 2 dnsparser.cc | 3 dnsparser.hh | 5 dnswriter.cc | 47 dnswriter.hh | 20 doh.cc | 51 doh3.cc | 60 doq-common.cc | 5 doq-common.hh | 7 doq.cc | 40 ext/luawrapper/include/LuaContext.hpp | 34 ext/yahttp/yahttp/reqresp.cpp | 29 ext/yahttp/yahttp/reqresp.hpp | 16 html/local.js | 255 - htmlfiles.h | 1356 ++++------ iputils.hh | 7 libssl.cc | 17 statnode.cc | 36 statnode.hh | 4 test-dnsdist-dnsparser.cc | 18 test-dnsdist-lua-ffi.cc | 50 test-dnsdistasync.cc | 32 test-dnsdistnghttp2_cc.cc | 26 test-iputils_hh.cc | 24 xsk.cc | 2 63 files changed, 1467 insertions(+), 1729 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpy124vk3d/dnsdist_1.9.10-1+deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpy124vk3d/dnsdist_1.9.14-0+deb13u1.dsc: no acceptable signature found diff -Nru dnsdist-1.9.10/Makefile.am dnsdist-1.9.14/Makefile.am --- dnsdist-1.9.10/Makefile.am 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/Makefile.am 2026-04-22 16:47:03.000000000 +0000 @@ -599,7 +599,7 @@ .venv: docs/requirements.txt $(PYTHON) -m venv .venv - .venv/bin/pip install -U pip setuptools setuptools-git wheel + .venv/bin/pip install -U pip "setuptools<82" .venv/bin/pip install -r $< latex/dnsdist.pdf: docs/** .venv diff -Nru dnsdist-1.9.10/Makefile.in dnsdist-1.9.14/Makefile.in --- dnsdist-1.9.10/Makefile.in 2025-05-20 09:13:46.000000000 +0000 +++ dnsdist-1.9.14/Makefile.in 2026-04-22 16:47:27.000000000 +0000 @@ -2854,7 +2854,7 @@ @HAVE_VENV_TRUE@.venv: docs/requirements.txt @HAVE_VENV_TRUE@ $(PYTHON) -m venv .venv -@HAVE_VENV_TRUE@ .venv/bin/pip install -U pip setuptools setuptools-git wheel +@HAVE_VENV_TRUE@ .venv/bin/pip install -U pip "setuptools<82" @HAVE_VENV_TRUE@ .venv/bin/pip install -r $< @HAVE_VENV_TRUE@latex/dnsdist.pdf: docs/** .venv diff -Nru dnsdist-1.9.10/configure dnsdist-1.9.14/configure --- dnsdist-1.9.10/configure 2025-05-20 09:13:44.000000000 +0000 +++ dnsdist-1.9.14/configure 2026-04-22 16:47:25.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for dnsdist 1.9.10. +# Generated by GNU Autoconf 2.71 for dnsdist 1.9.14. # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, @@ -618,8 +618,8 @@ # Identity of this package. PACKAGE_NAME='dnsdist' PACKAGE_TARNAME='dnsdist' -PACKAGE_VERSION='1.9.10' -PACKAGE_STRING='dnsdist 1.9.10' +PACKAGE_VERSION='1.9.14' +PACKAGE_STRING='dnsdist 1.9.14' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1645,7 +1645,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures dnsdist 1.9.10 to adapt to many kinds of systems. +\`configure' configures dnsdist 1.9.14 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1716,7 +1716,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of dnsdist 1.9.10:";; + short | recursive ) echo "Configuration of dnsdist 1.9.14:";; esac cat <<\_ACEOF @@ -1951,7 +1951,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -dnsdist configure 1.9.10 +dnsdist configure 1.9.14 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. @@ -2440,7 +2440,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by dnsdist $as_me 1.9.10, which was +It was created by dnsdist $as_me 1.9.14, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw @@ -3932,7 +3932,7 @@ # Define the identity of the package. PACKAGE='dnsdist' - VERSION='1.9.10' + VERSION='1.9.14' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -28627,7 +28627,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by dnsdist $as_me 1.9.10, which was +This file was extended by dnsdist $as_me 1.9.14, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -28695,7 +28695,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -dnsdist config.status 1.9.10 +dnsdist config.status 1.9.14 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" diff -Nru dnsdist-1.9.10/configure.ac dnsdist-1.9.14/configure.ac --- dnsdist-1.9.10/configure.ac 2025-05-20 09:13:35.000000000 +0000 +++ dnsdist-1.9.14/configure.ac 2026-04-22 16:47:15.000000000 +0000 @@ -1,6 +1,6 @@ AC_PREREQ([2.69]) -AC_INIT([dnsdist], [1.9.10]) +AC_INIT([dnsdist], [1.9.14]) AM_INIT_AUTOMAKE([foreign tar-ustar dist-bzip2 no-dist-gzip parallel-tests 1.11 subdir-objects]) AM_SILENT_RULES([yes]) AC_CONFIG_MACRO_DIR([m4]) diff -Nru dnsdist-1.9.10/debian/changelog dnsdist-1.9.14/debian/changelog --- dnsdist-1.9.10/debian/changelog 2025-09-12 08:39:35.000000000 +0000 +++ dnsdist-1.9.14/debian/changelog 2026-04-26 18:59:04.000000000 +0000 @@ -1,3 +1,15 @@ +dnsdist (1.9.14-0+deb13u1) trixie-security; urgency=medium + + * New upstream version 1.9.14, fixing (from 1.9.13) CVE-2026-33257, + CVE-2026-33260, CVE-2026-33596, CVE-2026-33597, CVE-2026-33598, + CVE-2026-33599, CVE-2026-33602, CVE-2026-33595, CVE-2026-33254, + CVE-2026-33594, CVE-2026-33593, (from 1.9.12) CVE-2026-0396, + CVE-2026-0397, CVE-2026-24028, CVE-2026-24029, CVE-2026-24030, + CVE-2026-27853, CVE-2026-27854. + * Drop upstream-applied patches. + + -- Chris Hofstaedtler Sun, 26 Apr 2026 20:59:04 +0200 + dnsdist (1.9.10-1+deb13u1) trixie; urgency=medium * d/{gbp.conf,.gitlab-ci.yml}: setup for trixie diff -Nru dnsdist-1.9.10/debian/gbp.conf dnsdist-1.9.14/debian/gbp.conf --- dnsdist-1.9.10/debian/gbp.conf 2025-09-12 08:39:35.000000000 +0000 +++ dnsdist-1.9.14/debian/gbp.conf 2026-04-26 18:59:04.000000000 +0000 @@ -3,3 +3,4 @@ multimaint-merge = True patch-numbers = False debian-branch = debian/trixie +upstream-branch = upstream/trixie diff -Nru dnsdist-1.9.10/debian/patches/series dnsdist-1.9.14/debian/patches/series --- dnsdist-1.9.10/debian/patches/series 2025-09-12 08:39:35.000000000 +0000 +++ dnsdist-1.9.14/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -upstream/CVE-2025-8671-CVE-2025-30187-1.9.10.patch diff -Nru dnsdist-1.9.10/debian/patches/upstream/CVE-2025-8671-CVE-2025-30187-1.9.10.patch dnsdist-1.9.14/debian/patches/upstream/CVE-2025-8671-CVE-2025-30187-1.9.10.patch --- dnsdist-1.9.10/debian/patches/upstream/CVE-2025-8671-CVE-2025-30187-1.9.10.patch 2025-09-12 08:39:35.000000000 +0000 +++ dnsdist-1.9.14/debian/patches/upstream/CVE-2025-8671-CVE-2025-30187-1.9.10.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,374 +0,0 @@ -From: Remi Gacogne -Date: Thu, 11 Sep 2025 13:38:49 +0200 -Subject: PowerDNS Security Advisory 2025-05 for DNSdist: Denial of service via crafted DoH exchange - -While working on adding mitigations against the MadeYouReset (CVE-2025-8671) -attack, we noticed a potential denial of service in our DNS over HTTPS -implementation when using the nghttp2 provider: an attacker might be able to -cause a denial of service by crafting a DoH exchange that triggers an unbounded -I/O read loop, causing an unexpected consumption of CPU resources. We assigned -CVE-2025-30187 to this issue. - -Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1115643 - - -diff -ruw dnsdist-1.9.10.orig/dnsdist-doh-common.hh dnsdist-1.9.10/dnsdist-doh-common.hh ---- dnsdist-1.9.10.orig/dnsdist-doh-common.hh 2025-05-20 11:13:25.000000000 +0200 -+++ dnsdist-1.9.10/dnsdist-doh-common.hh 2025-09-11 11:09:57.007006314 +0200 -@@ -35,6 +35,8 @@ - - namespace dnsdist::doh - { -+static constexpr uint32_t MAX_INCOMING_CONCURRENT_STREAMS{100U}; -+ - std::optional getPayloadFromPath(const std::string_view& path); - } - -diff -ruw dnsdist-1.9.10.orig/dnsdist-nghttp2-in.cc dnsdist-1.9.10/dnsdist-nghttp2-in.cc ---- dnsdist-1.9.10.orig/dnsdist-nghttp2-in.cc 2025-05-20 11:13:25.000000000 +0200 -+++ dnsdist-1.9.10/dnsdist-nghttp2-in.cc 2025-09-11 11:10:21.628205731 +0200 -@@ -288,7 +288,7 @@ - - void IncomingHTTP2Connection::handleConnectionReady() - { -- constexpr std::array settings{{{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100U}}}; -+ constexpr std::array settings{{{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, dnsdist::doh::MAX_INCOMING_CONCURRENT_STREAMS}}}; - auto ret = nghttp2_submit_settings(d_session.get(), NGHTTP2_FLAG_NONE, settings.data(), settings.size()); - if (ret != 0) { - throw std::runtime_error("Fatal error: " + std::string(nghttp2_strerror(ret))); -@@ -440,6 +440,24 @@ - if (nghttp2_session_want_read(d_session.get()) != 0) { - updateIO(IOState::NeedRead, handleReadableIOCallback); - } -+ else { -+ if (getConcurrentStreamsCount() == 0) { -+ d_connectionDied = true; -+ stopIO(); -+ } -+ else { -+ updateIO(IOState::Done, handleReadableIOCallback); -+ } -+ } -+ } -+ else { -+ if (getConcurrentStreamsCount() == 0) { -+ d_connectionDied = true; -+ stopIO(); -+ } -+ else { -+ updateIO(IOState::Done, handleReadableIOCallback); -+ } - } - } - catch (const std::exception& e) { -@@ -547,12 +565,22 @@ - NGHTTP2Headers::addCustomDynamicHeader(headers, name, value); - } - -+std::unordered_map::iterator IncomingHTTP2Connection::getStreamContext(StreamID streamID) -+{ -+ auto streamIt = d_currentStreams.find(streamID); -+ if (streamIt == d_currentStreams.end()) { -+ /* it might have been closed by the remote end in the meantime */ -+ d_killedStreams.erase(streamID); -+ } -+ return streamIt; -+} -+ - IOState IncomingHTTP2Connection::sendResponse(const struct timeval& now, TCPResponse&& response) - { - if (response.d_idstate.d_streamID == -1) { - throw std::runtime_error("Invalid DoH stream ID while sending response"); - } -- auto streamIt = d_currentStreams.find(response.d_idstate.d_streamID); -+ auto streamIt = getStreamContext(response.d_idstate.d_streamID); - if (streamIt == d_currentStreams.end()) { - /* it might have been closed by the remote end in the meantime */ - return hasPendingWrite() ? IOState::NeedWrite : IOState::Done; -@@ -592,7 +620,7 @@ - throw std::runtime_error("Invalid DoH stream ID while handling I/O error notification"); - } - -- auto streamIt = d_currentStreams.find(response.d_idstate.d_streamID); -+ auto streamIt = getStreamContext(response.d_idstate.d_streamID); - if (streamIt == d_currentStreams.end()) { - /* it might have been closed by the remote end in the meantime */ - return; -@@ -735,17 +763,18 @@ - NGHTTP2Headers::addCustomDynamicHeader(headers, key, value); - } - -+ context.d_sendingResponse = true; - auto ret = nghttp2_submit_response(d_session.get(), streamID, headers.data(), headers.size(), &data_provider); - if (ret != 0) { -- d_currentStreams.erase(streamID); - vinfolog("Error submitting HTTP response for stream %d: %s", streamID, nghttp2_strerror(ret)); -+ d_currentStreams.erase(streamID); - return false; - } - - ret = nghttp2_session_send(d_session.get()); - if (ret != 0) { -- d_currentStreams.erase(streamID); - vinfolog("Error flushing HTTP response for stream %d: %s", streamID, nghttp2_strerror(ret)); -+ d_currentStreams.erase(streamID); - return false; - } - -@@ -921,7 +950,7 @@ - /* is this the last frame for this stream? */ - if ((frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_DATA) && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) != 0) { - auto streamID = frame->hd.stream_id; -- auto stream = conn->d_currentStreams.find(streamID); -+ auto stream = conn->getStreamContext(streamID); - if (stream != conn->d_currentStreams.end()) { - conn->handleIncomingQuery(std::move(stream->second), streamID); - } -@@ -941,7 +970,16 @@ - { - auto* conn = static_cast(user_data); - -- conn->d_currentStreams.erase(stream_id); -+ auto streamIt = conn->d_currentStreams.find(stream_id); -+ if (streamIt == conn->d_currentStreams.end()) { -+ return 0; -+ } -+ -+ if (!streamIt->second.d_sendingResponse) { -+ conn->d_killedStreams.emplace(stream_id); -+ } -+ -+ conn->d_currentStreams.erase(streamIt); - return 0; - } - -@@ -952,20 +990,29 @@ - } - - auto* conn = static_cast(user_data); -- auto insertPair = conn->d_currentStreams.emplace(frame->hd.stream_id, PendingQuery()); -- if (!insertPair.second) { -- /* there is a stream ID collision, something is very wrong! */ -- vinfolog("Stream ID collision (%d) on connection from %d", frame->hd.stream_id, conn->d_ci.remote.toStringWithPort()); -- conn->d_connectionClosing = true; -- conn->d_needFlush = true; -- nghttp2_session_terminate_session(conn->d_session.get(), NGHTTP2_NO_ERROR); -- auto ret = nghttp2_session_send(conn->d_session.get()); -+ auto close_connection = [](IncomingHTTP2Connection* connection, int32_t streamID, const ComboAddress& remote) -> int { -+ connection->d_connectionClosing = true; -+ connection->d_needFlush = true; -+ nghttp2_session_terminate_session(connection->d_session.get(), NGHTTP2_REFUSED_STREAM); -+ auto ret = nghttp2_session_send(connection->d_session.get()); - if (ret != 0) { -- vinfolog("Error flushing HTTP response for stream %d from %s: %s", frame->hd.stream_id, conn->d_ci.remote.toStringWithPort(), nghttp2_strerror(ret)); -+ vinfolog("Error flushing HTTP response for stream %d from %s: %s", streamID, remote.toStringWithPort(), nghttp2_strerror(ret)); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - return 0; -+ }; -+ -+ if (conn->getConcurrentStreamsCount() >= dnsdist::doh::MAX_INCOMING_CONCURRENT_STREAMS) { -+ vinfolog("Too many concurrent streams on connection from %s", conn->d_ci.remote.toStringWithPort()); -+ return close_connection(conn, frame->hd.stream_id, conn->d_ci.remote); -+ } -+ -+ auto insertPair = conn->d_currentStreams.emplace(frame->hd.stream_id, PendingQuery()); -+ if (!insertPair.second) { -+ /* there is a stream ID collision, something is very wrong! */ -+ vinfolog("Stream ID collision (%d) on connection from %s", frame->hd.stream_id, conn->d_ci.remote.toStringWithPort()); -+ return close_connection(conn, frame->hd.stream_id, conn->d_ci.remote); - } - - return 0; -@@ -1002,7 +1049,7 @@ - return nameLen == expected.size() && memcmp(name, expected.data(), expected.size()) == 0; - }; - -- auto stream = conn->d_currentStreams.find(frame->hd.stream_id); -+ auto stream = conn->getStreamContext(frame->hd.stream_id); - if (stream == conn->d_currentStreams.end()) { - vinfolog("Unable to match the stream ID %d to a known one!", frame->hd.stream_id); - return NGHTTP2_ERR_CALLBACK_FAILURE; -@@ -1065,7 +1112,7 @@ - int IncomingHTTP2Connection::on_data_chunk_recv_callback(nghttp2_session* session, uint8_t flags, IncomingHTTP2Connection::StreamID stream_id, const uint8_t* data, size_t len, void* user_data) - { - auto* conn = static_cast(user_data); -- auto stream = conn->d_currentStreams.find(stream_id); -+ auto stream = conn->getStreamContext(stream_id); - if (stream == conn->d_currentStreams.end()) { - vinfolog("Unable to match the stream ID %d to a known one!", stream_id); - return NGHTTP2_ERR_CALLBACK_FAILURE; -@@ -1155,7 +1202,7 @@ - - uint32_t IncomingHTTP2Connection::getConcurrentStreamsCount() const - { -- return d_currentStreams.size(); -+ return d_currentStreams.size() + d_killedStreams.size(); - } - - boost::optional IncomingHTTP2Connection::getIdleClientReadTTD(struct timeval now) const -@@ -1208,6 +1255,9 @@ - ttd = getClientWriteTTD(now); - d_ioState->update(newState, callback, shared, ttd); - } -+ else if (newState == IOState::Done) { -+ d_ioState->reset(); -+ } - } - - void IncomingHTTP2Connection::handleIOError() -@@ -1217,6 +1267,7 @@ - d_outPos = 0; - nghttp2_session_terminate_session(d_session.get(), NGHTTP2_PROTOCOL_ERROR); - d_currentStreams.clear(); -+ d_killedStreams.clear(); - stopIO(); - } - -diff -ruw dnsdist-1.9.10.orig/dnsdist-nghttp2-in.hh dnsdist-1.9.10/dnsdist-nghttp2-in.hh ---- dnsdist-1.9.10.orig/dnsdist-nghttp2-in.hh 2025-05-20 11:13:25.000000000 +0200 -+++ dnsdist-1.9.10/dnsdist-nghttp2-in.hh 2025-09-11 11:10:04.764742240 +0200 -@@ -55,6 +55,7 @@ - size_t d_queryPos{0}; - uint32_t d_statusCode{0}; - Method d_method{Method::Unknown}; -+ bool d_sendingResponse{false}; - }; - - IncomingHTTP2Connection(ConnectionInfo&& connectionInfo, TCPClientThreadData& threadData, const struct timeval& now); -@@ -86,6 +87,7 @@ - std::unique_ptr getDOHUnit(uint32_t streamID) override; - - void stopIO(); -+ std::unordered_map::iterator getStreamContext(StreamID streamID); - uint32_t getConcurrentStreamsCount() const; - void updateIO(IOState newState, const FDMultiplexer::callbackfunc_t& callback); - void handleIOError(); -@@ -101,6 +103,7 @@ - - std::unique_ptr d_session{nullptr, nghttp2_session_del}; - std::unordered_map d_currentStreams; -+ std::unordered_set d_killedStreams; - PacketBuffer d_out; - PacketBuffer d_in; - size_t d_outPos{0}; -diff -ruw dnsdist-1.9.10.orig/doh.cc dnsdist-1.9.10/doh.cc ---- dnsdist-1.9.10.orig/doh.cc 2025-05-20 11:13:25.000000000 +0200 -+++ dnsdist-1.9.10/doh.cc 2025-09-11 11:10:16.325285812 +0200 -@@ -313,6 +313,7 @@ - struct timeval d_connectionStartTime{0, 0}; - size_t d_nbQueries{0}; - int d_desc{-1}; -+ uint8_t d_concurrentStreams{0}; - }; - - static thread_local std::unordered_map t_conns; -@@ -386,6 +387,17 @@ - return reasonIt->second; - } - -+static DOHConnection* getConnectionFromQuery(const h2o_req_t* req) -+{ -+ h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn); -+ const int descriptor = h2o_socket_get_fd(sock); -+ if (descriptor == -1) { -+ /* this should not happen, but let's not crash on it */ -+ return nullptr; -+ } -+ return &t_conns.at(descriptor); -+} -+ - /* Always called from the main DoH thread */ - static void handleResponse(DOHFrontend& dohFrontend, st_h2o_req_t* req, uint16_t statusCode, const PacketBuffer& response, const std::unordered_map& customResponseHeaders, const std::string& contentType, bool addContentType) - { -@@ -461,6 +473,10 @@ - - ++dohFrontend.d_errorresponses; - } -+ -+ if (auto* conn = getConnectionFromQuery(req)) { -+ --conn->d_concurrentStreams; -+ } - } - - static std::unique_ptr getDUFromIDS(InternalQueryState& ids) -@@ -918,6 +934,8 @@ - via a pipe */ - static void doh_dispatch_query(DOHServerConfig* dsc, h2o_handler_t* self, h2o_req_t* req, PacketBuffer&& query, const ComboAddress& local, const ComboAddress& remote, std::string&& path) - { -+ auto* conn = getConnectionFromQuery(req); -+ - try { - /* we only parse it there as a sanity check, we will parse it again later */ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) -@@ -949,6 +967,9 @@ - } - } - -+ if (conn != nullptr) { -+ ++conn->d_concurrentStreams; -+ } - #ifdef HAVE_H2O_SOCKET_GET_SSL_SERVER_NAME - h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn); - const char * sni = h2o_socket_get_ssl_server_name(sock); -@@ -966,17 +987,26 @@ - if (!dsc->d_querySender.send(std::move(dohUnit))) { - ++dnsdist::metrics::g_stats.dohQueryPipeFull; - vinfolog("Unable to pass a DoH query to the DoH worker thread because the pipe is full"); -+ if (conn != nullptr) { -+ --conn->d_concurrentStreams; -+ } - h2o_send_error_500(req, "Internal Server Error", "Internal Server Error", 0); - } - } - catch (...) { - vinfolog("Unable to pass a DoH query to the DoH worker thread because we couldn't write to the pipe: %s", stringerror()); -+ if (conn != nullptr) { -+ --conn->d_concurrentStreams; -+ } - h2o_send_error_500(req, "Internal Server Error", "Internal Server Error", 0); - } - #endif /* USE_SINGLE_ACCEPTOR_THREAD */ - } - catch (const std::exception& e) { - vinfolog("Had error parsing DoH DNS packet from %s: %s", remote.toStringWithPort(), e.what()); -+ if (conn != nullptr) { -+ --conn->d_concurrentStreams; -+ } - h2o_send_error_400(req, "Bad Request", "The DNS query could not be parsed", 0); - } - } -@@ -1046,15 +1076,19 @@ - } - // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): h2o API - auto* dsc = static_cast(req->conn->ctx->storage.entries[0].data); -- h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn); -- -- const int descriptor = h2o_socket_get_fd(sock); -- if (descriptor == -1) { -+ auto* connPtr = getConnectionFromQuery(req); -+ if (connPtr == nullptr) { -+ return 0; -+ } -+ auto& conn = *connPtr; -+ if (conn.d_concurrentStreams >= dnsdist::doh::MAX_INCOMING_CONCURRENT_STREAMS) { -+ vinfolog("Too many concurrent streams on connection from %d", conn.d_remote.toStringWithPort()); - return 0; - } - -- auto& conn = t_conns.at(descriptor); - ++conn.d_nbQueries; -+ -+ h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn); - if (conn.d_nbQueries == 1) { - if (h2o_socket_get_ssl_session_reused(sock) == 0) { - ++dsc->clientState->tlsNewSessions; -@@ -1121,6 +1155,7 @@ - for (const auto& entry : *responsesMap) { - if (entry->matches(path)) { - const auto& customHeaders = entry->getHeaders(); -+ ++conn.d_concurrentStreams; - handleResponse(*dsc->dohFrontend, req, entry->getStatusCode(), entry->getContent(), customHeaders ? *customHeaders : dsc->dohFrontend->d_customResponseHeaders, std::string(), false); - return 0; - } diff -Nru dnsdist-1.9.10/debian/upstream/signing-key.asc dnsdist-1.9.14/debian/upstream/signing-key.asc --- dnsdist-1.9.10/debian/upstream/signing-key.asc 2025-09-07 11:22:22.000000000 +0000 +++ dnsdist-1.9.14/debian/upstream/signing-key.asc 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBE5fJpEBEADl7Epp8pxg5ENBY4KM7U/lrxRg33BPDJcZTxqnCLbNCdEOSO1T -Ej3jWl1HEh236NlWLvHsXgrsKiB1jX037q62QKrp10trQMsM6QiEUjwmrGJxgxv2 -D/+U2PJPh6/ElFhx1PqGEC1Ih3pTpP1YINzfX6cQ9/e3nc64BcBTQqYA2/YIv4pH -MYXZrPm398JZbPpT0ot9ggdLulUYSRJQ9dfNJbGpstMMfOkA2IFvfmKc5BT5Y/ZA -ayF7xPBEGbBMLaZuT8q+x5S39ZyzxzCMSIJD7nYAh7qI0xiosfu8YyjXPN3x1OYX -kdBKzYEk8ji9xgyNZ/9Hlsq3JhJzuGKuXKuC3GKf8BcNw0JH+/VWmq+3kd30a8dy -GgCW+YJok+zyo51WWVLeJ//5tZ2/GvRhbIivA6Gp2cxQlwl9Ouj7SkBNRWvjBJUr -N0NF1FxKo1Yq9OECq2KgLn3l2vURW+LUtdXDbZcn9GcYbGHIE0xdCGQSH/H+Dkgl -T63rQIgBN2MTQ4lhun/5ABLq7s82BAtakhQ5S+3gD+LykCcvCxgHApV28yJJT3ZZ -+Qt6uNtHf2y6T4eJpiE+bWJpG3ujCwzQxu3x5L76jOgiRaj6HcwzT79LpjZMzhnK -1sKhDAuJP2VNIYhAXn8UF+z54dmBRK58t8zQVop+BpJAE7QM/DFDp3uLhwARAQAB -tC1QZXRlciB2YW4gRGlqayA8cGV0ZXIudmFuLmRpamtAbmV0aGVybGFicy5ubD6J -AjgEEwECACIFAk5fJpECGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJENz1 -E/p+7RnzoQQQAJjEVUbLcBd4blXL6EW3VMqIMFbxBt4CiHRjsSo02+rUMWLOqZBE -Rfynv0oufhrW3AqTO0OMoqPLWjWFNeOHOdKieBJdcXHDJPO8qRUpbcYh5CXr54X0 -9d5WZU8sGipnd8wxO68J8g+5vux3xscEaZTwWZTwyelWA77OxJm6WlPPxJ+lTyIu -hVC3KoBUWRwfNrxE/ij/0tkVFoIXvczbAQqB6+nApHZvtoR4Wys4bzmCWuo9PUj0 -r3+eyjsWEB0A4Ya1bwaJOchubi/Gq99wfp71zJC8FcSMWmoGPRnpg6oLpkxC8Yre -V/16DUgiMnxUPyJAEpb+AH0MMudmp6tnUaWBs/hWnpyWPXqjt6wzs7X31X2oj93A -NKjnSpglOgUEBKk4GTyOuBo3S+kyXD9WW977kyKVtUQf3U5EHUR08UA/DuEJPGDn -Ma9lujXM17h//iyixa0RhJXX+ZRKRwEAZqj6H8wNayF045JdwMJ6TIePuymV2lty -G5E0M5l5SOc4fELNHJyHvjhi1Fb23lqBxNhvdm8+RtwtFz+QtFwihP/cEBMue5lc -j5Bkvwx3NERJxoPi/Qe82mLZLaMCdlP++jzvSrsVrRWkyw+i08T0+Dp9/V5YoEUk -hSfNp1w26FtrFVqC4XpVxtjda32Ipw3aygpOqEkCxNsy3+C1buzr/QK9tCxQZXRl -ciB2YW4gRGlqayA8cGV0ZXIudmFuLmRpamtAcG93ZXJkbnMuY29tPokCOAQTAQIA -IgUCVMofzwIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ3PUT+n7tGfOj -hg/+NtAiH9AVGTmbpdNHNyWrmPxgO5XKtfLZ+4gz6D1QpFwFO/YPL9iN8RhzsZJC -estI6tP9vuBPvku7Nd1IFfuZlUmg26gftUTQUmRMd/lMm264WOYPXu12g8+PvkUw -XfyoAcd32nOpSMkpiFymRN8GtTzIm4qOgWA2+mdFWMl0xTSuKv60MiNIszEKD80U -xDS2bSj1cv2VBxDFwmlrzPEa/ozxAI9t9CxY8Lv6CsEfr5yHSWOkV/mqSo+4Oegd -NjiRRoeMo0/bUBOtkZSykj2ONSVBn1oIOYQtForUhtRyZFLJIiO11szngRDqRYOs -lmMMLuZ2k+/b/K4E9jvJvz7yt+Y3BPOsjRtV9tP7oSKJpTN43PTnTbKMM2RpVTN3 -bPtfhZ1+iQubk0y2H2XaKOnKX6wEdjaWOoBR6SRzVzSz8dgOqvkfBhA/bJchwWHn -yckk7bZJXMszafnkJZzW/eVeuopUgkyWeMHp6lngpwpSqCPXn10zm3bBYNSOFdCC -X2Qs3hB8fLi8OQGv9puikZmvdIwL1jWP1GEkkP94xFTtv4wqgBykySjTMBs9zEDO -OGq2x2ndUJZuUpoY7AHBtzmMrfwuuHwyFHfTVpalE36S6f7D8UA631vO/BGcDj+5 -xnAhhTBiiFbworNjAfZs4/bfKam4v5rYb+riz+OJYVa0m9K5Ag0ETl8mkQEQAM4W -IsHIK/1+/39QZbh376iVXfc4NVdE3ID/Lozz9JDanjkpScpikwugDwguVx+8JdO2 -tTyo6JTzpiZ+CoaxmjudJpUTT7fD5ONcAd1stpHKUQFwJczU6LSXpTQCpmhV5s13 -pwumxjymKRlotxLdr9+zxFl0e4VTFb5oj4Ik2wu6sehcIt73AxM38C8smFRrRegP -QL2Xnq9BE+WUF2yyY3TOVAK5TP2MbwQTkrTOiTYJZdNHNlvjIpZaxHKOLqytNXSm -Xn1k20nitmyssIzv0aEC1UdktWIL/gD1Z+SjrJQB7/y56Dx7o6gr6J2MZZeo7a21 -1TLdblejD6bMjGaH4CTnjzmkMtDC/2b+FUc3x3/GlQF4hWB4iaT4aCjiKOVNQgaQ -yAeRTsv1BUoqf8LDytW1/MdalLYElKS77t69HEQ9HSyt7QHU3sjAG6qgso8yWn8e -bYCefm1lyZSP3BbvZ/UpoKuB+aGlXjteaXQhIRLRA1TgijiGA3Yw1dTcz2Cb42w4 -UNZw4r55yN60QDRBH4l1yrRPltdyAaX3qEg44U/Z7LU2YTDX+4JL1O4ZE+snDVsT -PMpuZLvRFkxCLG1FTXZacZRXfzlFzw6YWhpnHUYORO3fGhb+PKMKYEloTyLywjkV -LHFbvaPts96dCxWyDrcMOqhgiLOLJo7qC+/Sq8k9ABEBAAGJAh8EGAECAAkFAk5f -JpECGwwACgkQ3PUT+n7tGfNv1A//dYWV+vL1jiL+X4vRSCrDM8bBmt/cZfN5O0i3 -HYPMdSD9lVr9O+WYKJogxEXX1ofgEO74rwZxGw0crrMN8VM9SgMZ3jioGI15NF3I -NnA1r53GNGhJ4JVnz0KV2NKtshk7CtSxrjoR8qplwbMMICVgTIERVP1enuOb3FEt -bhI4rcy+2UTw3hwURBhIfUotVFO6SKu3ZLscItbiNxpTqTpL6AIp9UOrZjcqfCuF -s8P+57uusAHcp6GYhhIhNIdXf64RQs7gtdLVW71z0diSxu3KFWlrXOx0rrm7RTAQ -n1VOLl4W5oBPvcF2ZVQvd84I74TMtpP0MRDFgLuK0HHFVyDff0vx76rubQgom6z8 -ajiIa6MfEmd7z9xhQT5PU0FApYY6H/kW7ao+f2h2IIjz/+QjHuYn0CqqcjkkLC76 -RAgQjHYO9NIpL9Gi9O+I2AFz8YjOK3hOpxMrF/LjPJtxBXGFEwP4ud+hzDMjwaa7 -PklcmDPUBuSDIgbNvsVNA6gn7AkbQn6NH+DImdrpzgpSr1FHMbjIWqpXWbAZtmOu -rxn9f5ZXPKAgMvlV4TS4NZqnWT5HZCKs2b5Ped2L+zAdLP5NmyzJrSIyVTJ7JMLL -fCLaWu/qsHRGt1w86gewg7uMPdA1IEvjjXaIWNhYKUq6ik+DNrq0Y3fUuRg35QHa -PTcab+eZAQ0EVjikBwEIAIhTkdGQEbdVwF8lqp63Eigp0tHFbdeZ4LCu4sW3oM3e -rxtO2w25Awkdrw5jRopYmheM5BJsGgpIZUAUpOakJR8fi+ESu3wNarKCVF+KjYvd -xN7jwZmOI5t1ctnGewg0DHZZtymgJEpON1ZfQwfYmD/J/k9Lqdv6CVyVGwNCZUZC -O33a/bec12wKnwj2uM/X5tDLmIcHUiJC4UnoMFAmGBZDOSxPZrNnzdoAO9zj/4WD -tUVhLNkeSn3w1/LNSSJTNiLQjk7Lgq/Khd5L8Jf1a1AYzW+NkBdeIP44MnQ68HYS -wJRPq3iL2lZaH/4uc21FYhWfw8l5BsIA7bAmUzFfbwEAEQEAAbQoUmVtaSBHYWNv -Z25lIDxyZW1pLmdhY29nbmVAcG93ZXJkbnMuY29tPokBPQQTAQoAJwUCVrBxMgIb -AwUJEswDAAULCQgHBAUVCgkICwUWAgMBAAIeAQIXgAAKCRCiCO1PivWERnTOB/4j -Lvex0M+TE5iL/FUki8EHyj6648sOCHnUHHnS+slME2b71iAvLJxClDJjLD43Jj7F -L0hu2LOnw+5PQZrhLyB1WEa1tC0tLvIkPuzCVJPI4FH7+AegmBrGYN6554Hy0C/Y -RF8mOGngL58hrumJTgjB7vC+CvDp0714WQG/SgcKqk4jkIz/Iep2vj3dCifdh+kJ -kaK/nnzIT1euiOzp8xLByiVbCOdlbvYoVetqvJcqIhOHCglv045lZcAp9kP9pm/k -EzHM34PhkH6SrR/uodshOH4p3Ux0wGgwUbouDvHUtjlK+GB8cYXdRny0tvdGBYUO -7CsFNzPoRC8CvD+VY8DltC1HYWNvZ25lLCBSZW1pIDxyZW1pLmdhY29nbmVAb3Bl -bi14Y2hhbmdlLmNvbT6JATEEEwECABsFAlY4pAcCGwMECwkIBwYVCAIJCgsFCRLM -AwAACgkQogjtT4r1hEbMMAf/WS0+yuheoWrxCZ4qYQo+AjlaenFTPQwrEDNioj6g -jST/eAaQW1/+trFPzwNrBSenDE6bwPcPdL51mXg+30fNzHLWrBPDsMqBlPTIvpBb -Q/bVqjV3JnU8I8dHfdKmInJRrCJM21gDTprQdqfBfSHJHgM5TG2+fUxpdLIAhBRk -nXt4+TuE272DJf6gHxnDs1oqQ6kAxC0ANJyEufFXJGeERN2OsFtSygOcUiHeXwWy -M77RGf73gkS9+bCoftiuM4gbKSibk4BbUVBZJCs28fDnAsmIstZldUGZgIuy0vUf -H153DTJflN+CIGEvRUwk+nrDIwYkV0pr9eZ0lz/OFhwzJ7kBDQRWOKQHAQgAjr1x -EZh1yglszi94+HLNFcgRPgRNktg2vxOGf64dAreJvL5iDrS2lrFMknh5BNuj7nJZ -2r40OOS91oH1qkVk+v9Cyo/3xwCpCOPQCkhzHpuQWXoMGMw/3/0tG6zTxnYdC999 -faCH0lLA8oDwHCHlZSHgsH9+qSNyjaJXvS+HVoGYzyuanU6OTM7EM5c7RCPhNjT9 -JzHLISnwaxgDpwi7Ez6yudcrg6DqS/uUwkyNtWyesx1DF9y2VJUNwa4NKIJkSH+n -iEoxK9NBfBAmAKc4o5+KPs6BvpvpiYY9gTKaaLypPHNcveQTDFv/26XHyzrCZmwu -GlcYBjboH/BWzKbhuQARAQABiQExBBgBAgAbBQJWOKQHAhsMBAsJCAcGFQgCCQoL -BQkSzAMAAAoJEKII7U+K9YRGXJQH/3PtQG0AkrXOpkOMXFLTKdCEViNNHN94VIac -eVn60zbmXzxhYeKz7K345/EqATi3P3/yDHcht7j3uYPhvaMjy3smN6vEwX7Ue40P -bFDWmm8mHpLdlOfPXF0SRUD8KTSD6+W2VJfEcDI6DDfUmCx9yYZ1U5u+O8Aj+1l2 -gdQbgAioPnQgqzf43qgnRcsfNmsVsXg7EbHspRpJOR1XyXl/9KrDP7p6kjwWTQ1N -oRjCw0qaX93odLeKIpd2riShlB7GteUTps0IfuiL94CA58PV2YvZapN1KmwDohHU -8rndN7zte7jbCyv1Vv9tP6Ns0TvycBAqlOZYdgabrT+Pccb4jCeZAg0EVPRvsgEQ -AMeXMm92zU2ooQOE4AbQhYY3gn+MG87l088WrAMlpTfbH7jBPDQ47EJyAVh3NY+X -XucXCMLzJ5e9sAIJk3PrYqDmjWVYDox3Hx5rMKIY65N1Rud1kMGWsgQCzU5RmarF -NLJ0OdpE0K0tMTajS3gxqJ1zOOKdSfZGS+u2+UKyLUelB07mZROv9uanu/ia8I/m -8RG5jb6pVzUpuoWW0J5XQoA6mvWREbJDgP0sWWgWSvt+0XRtrcHR9sie7a4ynjow -L6M+iTm4ShPrqX5TuxmwJSQcfTZjqz+5cnppyTzj+mG2/jHBERGWkL3sx37s3uoh -hWt2EZVuyIcUQMigGssCp9216K+ihyC4tEj8RSfbon35t7OGYJlRS7V/raIm4GdY -LCOkQs0yUIila6AcC5xpRnHXHIXvUNHrk1nAyz5PEER/6BiW+vMObonx+GR8oUfo -+2uMg4LSYKx+o5jgWBFiSdNl3gQK5+RswpdFfzyq9gZf4WvVOCdBH0YKEQ8iJYX1 -7drkn4OWMG7u0QnQs6GdZTcClQJMTWVBIaCtRMNCxuKX6XI9WOgwj6vHM/ijeugP -LOsUSv+uWcK/fH7SSqmtJdMmpwNKFUmr4ZTaWZ5QLLv6kXbIoKqEKFwXkJAPgm36 -mVEE+ruy3FoNl0um9S0W8tGXgs7pMmM0AZ/nHb6R++6lABEBAAG0KFBpZXRlciBM -ZXhpcyA8cGlldGVyLmxleGlzQHBvd2VyZG5zLmNvbT6JAj4EEwECACgFAlT0b7IC -GwMFCQlmAYAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEF5QcVvy/+GnS1YQ -AMbqFxpsYGMsVRpwrFZ1/NXyfekLk7XCWD++YgwZ10d8jXQAv0BHl5ubkpCEuZdb -7SqEkLgKbjRfw4KDqri7iDGjwoYckmqh1xl20qyTYkfeQnKizuBWmNsVL0JT0xUz -KhcHBh1bWx4FPF+iojOlZQLKwViPpGOacstlBcPfRPQhaP7QLKrOVvVQd0ebfbbs -jSBP+olik6OWRiyXiIfsAmGq0OFDtk+f7jI2UIMC+/ADpulzMmJdr8l28wgoudtV -M+w4LB6hbFOYSvVy+kcMqWI+yQ64Dy6OnFVI5cZH3jxQXviqEs+QNzM4jUQ37Kp3 -TPBEDvHNQkdYllk7E24ecF/bi/3kDISBfVobSdMGMERxPJhnNWCG3sEP+WPT5beD -KywcWX4kOHpmhL3wRxJbZzEusA7IXyeeb2AWfsJBdMtM5Ur2u4yW9Dq9uNFDvXvo -4mQXqM0aPmpKN5IrSzTBT/9bKvu6iOI+JBiEY/A5wCySrqIsjqeJ5Wac6dPCcNsx -LV1qdEVMV2eg86A/lbymPiQg5xJzF6RSFwmVpbye7HFqcKTLwbZJHzf46CpgFyK0 -Wuavp90Nxrr0oTaHxfnAHlTDdXMybm3Z7wlEXEg2hSh5rrTKYfjgtoIJRkCjaRh4 -0jRkIM0YLx5uR5PBk6hAzjik/7sYvgG5XuPra3L6Y8XGuQINBFT0b7IBEACjecdg -3e1IF3zBMadFbFah7ZSPFK4Y8Q+OMbeiu0TzXP65rRXDQi595jdIcQY6/7gB1Igu -qC0HWUo/Ns7GFnNnWbrAaoVWpLjHXgMJ9hqdyIEgluoJECH53d0Y73oi+PBoYUU5 -z2tHi7AiiJc9qMG4m9q2P7xUrnqCqmGO4pU9nFJTFUAodf/ioNk9EdmciLmFUm7X -kHNtUcKVQGWER7videdWLW1fhHAzhI1hYkN85ZfIULfrVNZBn1U/L4nry7P7HO0I -QxoK7POs6apxU4JyATEyvsnjYU+UOCDPXRIKHAZ4joEnFhyHPyURgdMLxQb0s1hn -bTEC+szvqb9kC0rCan1GRb6/VeW9eRi1CoBpHtQEwY6k+YgWpvcfR0w9+6BH5aqy -pGWnNDCWcOTINUrouALb68oxgnEAowhWIa0ujUYy+PMYF0AFArjLVxu1IBKaMD/W -sk0ws389xAnbVW81bhHN2Ye2NznDe3YfK5FkUyWXO6GA1tFQw+joxt6+TPcTxRJL -S/MG/gXcluQE3Kv+jteqi/dbt5A+potX6qGN+F1GJwD/mQKyULklzlcZCIYZN9On -KVbSxfn2xQ89bjvkNvRjuO33x0IozIr/R/uz4T0H9Ve4UoNj2vT4pH/Ba/ergQSf -rrAJMDyIB+SRIgY7LCQFB3rOIvg/HiqAY3VL1wARAQABiQIlBBgBAgAPBQJU9G+y -AhsMBQkJZgGAAAoJEF5QcVvy/+Gng+UP/0CjLMF30xjRim/+/qzx+2OZ1S6R6B2m -p971lQxB8gCA7dn80UhSZZMHfMeo2N34itI4HEhQb+2jTOgQvNjv36zMppZjHQUg -4+xabvZU33FrB2hhD/ZdNTm7lCD87vKxz07flApkscw20VenY8E81z15GuWLK/Uq -E9wK5sbVoFB36mwNFqgh8W3oBBJTJoxWBFnqZu7arsaXhEWpVW2+36I2SWaWFmIP -wrUbuwIXSuv+h+ksEOdVXr87AgxX4qsPs44N6z57yhCIz6g3ow7R06IolsDSE8wy -OWL313X6UE1R5QyqAX1yQ0BtmcPRh05SC/vRe7WeP2q+TyHMrM1/YLN/W9X4Y7lo -PL38fpmWMEaNT/RgZPFjqDbqxYpyN6Kymdsfr3YPYNrzcYlc0WRplvpZh3D67PhI -9XuRsb2c7GAU1jVze9Za1ZrPpcCCJgp562I9E1D+a4x7w9fsiGDkPOm5Iy3HTg9F -H8VUWM3uwret74ZlQyQkE1XYgRjqHrjqJOajJg8Qw4meItY0QB/5kxmAW1h96OoK -BUZq5GaQ8AhtPnH+4peGQHG9fvSL58pukqeLGHkSwgdMPIFYZTHiIDt2tVkbi7vE -3uvKPm1bZpvM2T6m9ZUkVWV39P1W9lkqWvXSVfit1GRUpFd2onM7Rs0jxbZ9VfiR -i2OblZ9Wkvts -=m7Xq ------END PGP PUBLIC KEY BLOCK----- diff -Nru dnsdist-1.9.10/debian/watch dnsdist-1.9.14/debian/watch --- dnsdist-1.9.10/debian/watch 2025-09-12 08:39:35.000000000 +0000 +++ dnsdist-1.9.14/debian/watch 2026-04-26 18:59:04.000000000 +0000 @@ -1,2 +1,2 @@ version=4 -opts=pgpsigurlmangle=s/$/.asc/,versionmangle=s/-/~/ https://downloads.powerdns.com/releases/ dnsdist-([0-9]+.*)\.tar\.bz2 debian uupdate +opts=versionmangle=s/-/~/ https://downloads.powerdns.com/releases/ dnsdist-(1\.[0-9]+.*)\.tar\.bz2 debian uupdate diff -Nru dnsdist-1.9.10/dnscrypt.cc dnsdist-1.9.14/dnscrypt.cc --- dnsdist-1.9.10/dnscrypt.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnscrypt.cc 2026-04-22 16:47:03.000000000 +0000 @@ -627,6 +627,10 @@ assert(d_header.clientNonce); assert(d_pair != nullptr); + if (unpaddedLen > maxLen) { + throw std::runtime_error("Trying to compute the padding size for an oversized content"); + } + unsigned char nonce[DNSCRYPT_NONCE_SIZE]; memcpy(nonce, d_header.clientNonce, (DNSCRYPT_NONCE_SIZE / 2)); memcpy(&(nonce[DNSCRYPT_NONCE_SIZE / 2]), d_header.clientNonce, (DNSCRYPT_NONCE_SIZE / 2)); @@ -677,6 +681,9 @@ size_t requiredSize = sizeof(responseHeader) + DNSCRYPT_MAC_SIZE + response.size(); size_t maxSize = std::min(maxResponseSize, requiredSize + DNSCRYPT_MAX_RESPONSE_PADDING_SIZE); + if (requiredSize > maxResponseSize) { + return ENOBUFS; + } uint16_t paddingSize = computePaddingSize(requiredSize, maxSize); requiredSize += paddingSize; diff -Nru dnsdist-1.9.10/dnsdist-backend.cc dnsdist-1.9.14/dnsdist-backend.cc --- dnsdist-1.9.10/dnsdist-backend.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-backend.cc 2026-04-22 16:47:03.000000000 +0000 @@ -557,7 +557,7 @@ do { uint16_t selectedID = (idOffset++) % idStates.size(); - IDState& ids = idStates[selectedID]; + IDState& ids = idStates.at(selectedID); auto guard = ids.acquire(); if (!guard) { continue; @@ -600,7 +600,7 @@ return; } - auto& ids = idStates[id]; + auto& ids = idStates.at(id); auto guard = ids.acquire(); if (!guard) { /* already used */ @@ -639,11 +639,11 @@ return result; } - if (id > idStates.size()) { + if (id >= idStates.size()) { return result; } - auto& ids = idStates[id]; + auto& ids = idStates.at(id); auto guard = ids.acquire(); if (!guard) { return result; diff -Nru dnsdist-1.9.10/dnsdist-console.cc dnsdist-1.9.14/dnsdist-console.cc --- dnsdist-1.9.10/dnsdist-console.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-console.cc 2026-04-22 16:47:03.000000000 +0000 @@ -797,6 +797,7 @@ { "SetECSPrefixLengthAction", true, "v4, v6", "Set the ECS prefix length. Subsequent rules are processed after this action" }, { "SetMacAddrAction", true, "option", "Add the source MAC address to the query as EDNS0 option option. This action is currently only supported on Linux. Subsequent rules are processed after this action" }, { "SetEDNSOptionAction", true, "option, data", "Add arbitrary EDNS option and data to the query. Subsequent rules are processed after this action" }, + { "SetEDNSOptionResponseAction", true, "option, data", "Add arbitrary EDNS option and data to the response. Subsequent rules are processed after this action"}, { "SetExtendedDNSErrorAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to the response corresponding to the current query. Subsequent rules are processed after this action" }, { "SetExtendedDNSErrorResponseAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to this response. Subsequent rules are processed after this action" }, { "SetNoRecurseAction", true, "", "strip RD bit from the question, let it go through" }, diff -Nru dnsdist-1.9.10/dnsdist-crypto.cc dnsdist-1.9.14/dnsdist-crypto.cc --- dnsdist-1.9.10/dnsdist-crypto.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-crypto.cc 2026-04-22 16:47:03.000000000 +0000 @@ -287,8 +287,8 @@ { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) auto* ptr = reinterpret_cast(value.data()); - uint32_t count = htonl(*ptr); - *ptr = ntohl(++count); + uint32_t count = htonl(*ptr) + 1; + *ptr = ntohl(count); } #else diff -Nru dnsdist-1.9.10/dnsdist-discovery.cc dnsdist-1.9.14/dnsdist-discovery.cc --- dnsdist-1.9.10/dnsdist-discovery.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-discovery.cc 2026-04-22 16:47:03.000000000 +0000 @@ -216,7 +216,7 @@ } /* we prefer the address we already know, whenever possible */ - if (tentativeAddresses.count(existingAddr) != 0) { + if (tentativeAddresses.empty() || tentativeAddresses.count(existingAddr) != 0) { tempConfig.d_addr = existingAddr; } else { diff -Nru dnsdist-1.9.10/dnsdist-doh-common.hh dnsdist-1.9.14/dnsdist-doh-common.hh --- dnsdist-1.9.10/dnsdist-doh-common.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-doh-common.hh 2026-04-22 16:47:03.000000000 +0000 @@ -35,6 +35,9 @@ namespace dnsdist::doh { +static constexpr uint32_t MAX_INCOMING_CONCURRENT_STREAMS{100U}; +static constexpr size_t MAX_INCOMING_HTTP_HEADERS{256U}; + std::optional getPayloadFromPath(const std::string_view& path); } diff -Nru dnsdist-1.9.10/dnsdist-dynblocks.hh dnsdist-1.9.14/dnsdist-dynblocks.hh --- dnsdist-1.9.10/dnsdist-dynblocks.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-dynblocks.hh 2026-04-22 16:47:03.000000000 +0000 @@ -62,6 +62,7 @@ { } + mutable std::string fullname; const StatNode& node; const StatNode::Stat& self; const StatNode::Stat& children; diff -Nru dnsdist-1.9.10/dnsdist-lua-actions.cc dnsdist-1.9.14/dnsdist-lua-actions.cc --- dnsdist-1.9.10/dnsdist-lua-actions.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-lua-actions.cc 2026-04-22 16:47:03.000000000 +0000 @@ -1101,6 +1101,31 @@ std::string d_data; }; +class SetEDNSOptionResponseAction : public DNSResponseAction +{ +public: + // this action does not stop the processing + SetEDNSOptionResponseAction(uint16_t code, std::string data) : + d_code(code), d_data(std::move(data)) + { + } + + DNSResponseAction::Action operator()(DNSResponse* response, std::string* ruleresult) const override + { + setEDNSOption(*response, d_code, d_data); + return Action::None; + } + + [[nodiscard]] std::string toString() const override + { + return "add EDNS Option (code=" + std::to_string(d_code) + ")"; + } + +private: + uint16_t d_code; + std::string d_data; +}; + class SetNoRecurseAction : public DNSAction { public: @@ -1856,7 +1881,6 @@ std::optional d_exportExtendedErrorsToMeta{std::nullopt}; bool d_includeCNAME; }; - #endif /* DISABLE_PROTOBUF */ class DropResponseAction : public DNSResponseAction @@ -2510,6 +2534,10 @@ return std::shared_ptr(new SetEDNSOptionAction(code, data)); }); + luaCtx.writeFunction("SetEDNSOptionResponseAction", [](int code, const std::string& data) { + return std::shared_ptr(new SetEDNSOptionResponseAction(code, data)); + }); + luaCtx.writeFunction("PoolAction", [](const std::string& poolname, boost::optional stopProcessing) { return std::shared_ptr(new PoolAction(poolname, stopProcessing ? *stopProcessing : true)); }); diff -Nru dnsdist-1.9.10/dnsdist-lua-bindings-network.cc dnsdist-1.9.14/dnsdist-lua-bindings-network.cc --- dnsdist-1.9.10/dnsdist-lua-bindings-network.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-lua-bindings-network.cc 2026-04-22 16:47:03.000000000 +0000 @@ -26,7 +26,7 @@ #include "dnsdist-lua-network.hh" #include "dolog.hh" -void setupLuaBindingsNetwork(LuaContext& luaCtx, bool client) +void setupLuaBindingsNetwork(LuaContext& luaCtx, bool client, bool configCheck) { luaCtx.writeFunction("newNetworkEndpoint", [client](const std::string& path) { if (client) { @@ -92,8 +92,8 @@ }); }); - luaCtx.registerFunction::*)()>("start", [client](std::shared_ptr& listener) { - if (client) { + luaCtx.registerFunction::*)()>("start", [client, configCheck](std::shared_ptr& listener) { + if (client || configCheck) { return; } diff -Nru dnsdist-1.9.10/dnsdist-lua-ffi-interface.h dnsdist-1.9.14/dnsdist-lua-ffi-interface.h --- dnsdist-1.9.10/dnsdist-lua-ffi-interface.h 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-lua-ffi-interface.h 2026-04-22 16:47:03.000000000 +0000 @@ -138,6 +138,8 @@ void dnsdist_ffi_dnsquestion_set_max_returned_ttl(dnsdist_ffi_dnsquestion_t* dq, uint32_t max) __attribute__ ((visibility ("default"))); bool dnsdist_ffi_dnsquestion_set_restartable(dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default"))); +bool dnsdist_ffi_dnsquestion_set_alternate_name(dnsdist_ffi_dnsquestion_t* dnsQuestion, const char* alternateName, size_t alternateNameSize, const char* tag, size_t tagSize, const char* tagValue, size_t tagValueSize, const char* formerNameTagName, size_t formerNameTagSize) __attribute__ ((visibility ("default"))); + typedef struct dnsdist_ffi_servers_list_t dnsdist_ffi_servers_list_t; typedef struct dnsdist_ffi_server_t dnsdist_ffi_server_t; diff -Nru dnsdist-1.9.10/dnsdist-lua-ffi-interface.inc dnsdist-1.9.14/dnsdist-lua-ffi-interface.inc --- dnsdist-1.9.10/dnsdist-lua-ffi-interface.inc 2025-05-20 09:13:53.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-lua-ffi-interface.inc 2026-04-22 16:47:33.000000000 +0000 @@ -139,6 +139,8 @@ void dnsdist_ffi_dnsquestion_set_max_returned_ttl(dnsdist_ffi_dnsquestion_t* dq, uint32_t max) __attribute__ ((visibility ("default"))); bool dnsdist_ffi_dnsquestion_set_restartable(dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default"))); +bool dnsdist_ffi_dnsquestion_set_alternate_name(dnsdist_ffi_dnsquestion_t* dnsQuestion, const char* alternateName, size_t alternateNameSize, const char* tag, size_t tagSize, const char* tagValue, size_t tagValueSize, const char* formerNameTagName, size_t formerNameTagSize) __attribute__ ((visibility ("default"))); + typedef struct dnsdist_ffi_servers_list_t dnsdist_ffi_servers_list_t; typedef struct dnsdist_ffi_server_t dnsdist_ffi_server_t; diff -Nru dnsdist-1.9.10/dnsdist-lua-ffi.cc dnsdist-1.9.14/dnsdist-lua-ffi.cc --- dnsdist-1.9.10/dnsdist-lua-ffi.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-lua-ffi.cc 2026-04-22 16:47:03.000000000 +0000 @@ -879,43 +879,19 @@ return dnsdist::queueQueryResumptionEvent(std::move(query)); } -bool dnsdist_ffi_resume_from_async_with_alternate_name(uint16_t asyncID, uint16_t queryID, const char* alternateName, size_t alternateNameSize, const char* tag, size_t tagSize, const char* tagValue, size_t tagValueSize, const char* formerNameTagName, size_t formerNameTagSize) +static bool setAlternateName(PacketBuffer& packet, InternalQueryState& ids, std::string_view alternateName, std::string_view tag, std::string_view tagValue, std::string_view formerNameTagName) { - if (!dnsdist::g_asyncHolder) { - return false; - } - - auto query = dnsdist::g_asyncHolder->get(asyncID, queryID); - if (!query) { - vinfolog("Unable to resume with an alternate name, no object found for asynchronous ID %d and query ID %d", asyncID, queryID); - return false; - } - - auto& ids = query->query.d_idstate; - DNSName originalName = ids.qname; - + const DNSName originalName = ids.qname; try { - DNSName parsed(alternateName, alternateNameSize, 0, false); - - PacketBuffer initialPacket; - if (query->d_isResponse) { - if (!ids.d_packet) { - return false; - } - initialPacket = std::move(*ids.d_packet); - } - else { - initialPacket = std::move(query->query.d_buffer); - } + DNSName parsed(alternateName.data(), alternateName.size(), 0, false); // edit qname in query packet - if (!dnsdist::changeNameInDNSPacket(initialPacket, originalName, parsed)) { + if (!dnsdist::changeNameInDNSPacket(packet, originalName, parsed)) { return false; } - if (query->d_isResponse) { - query->d_isResponse = false; + if (ids.d_packet) { + *ids.d_packet = packet; } - query->query.d_buffer = std::move(initialPacket); // set qname to new one ids.qname = std::move(parsed); } @@ -925,26 +901,73 @@ } // save existing qname in tag - if (formerNameTagName != nullptr && formerNameTagSize > 0) { + if (!formerNameTagName.empty()) { if (!ids.qTag) { ids.qTag = std::make_unique(); } - (*ids.qTag)[std::string(formerNameTagName, formerNameTagSize)] = originalName.getStorage(); + (*ids.qTag)[std::string(formerNameTagName)] = originalName.getStorage(); } - if (tag != nullptr && tagSize > 0) { + if (!tag.empty()) { if (!ids.qTag) { ids.qTag = std::make_unique(); } - (*ids.qTag)[std::string(tag, tagSize)] = std::string(tagValue, tagValueSize); + (*ids.qTag)[std::string(tag)] = tagValue; } ids.skipCache = true; + return true; +} + +bool dnsdist_ffi_resume_from_async_with_alternate_name(uint16_t asyncID, uint16_t queryID, const char* alternateName, size_t alternateNameSize, const char* tag, size_t tagSize, const char* tagValue, size_t tagValueSize, const char* formerNameTagName, size_t formerNameTagSize) +{ + if (!dnsdist::g_asyncHolder) { + return false; + } + + auto query = dnsdist::g_asyncHolder->get(asyncID, queryID); + if (!query) { + vinfolog("Unable to resume with an alternate name, no object found for asynchronous ID %d and query ID %d", asyncID, queryID); + return false; + } + + auto& ids = query->query.d_idstate; + PacketBuffer packet; + if (query->d_isResponse) { + if (!ids.d_packet) { + return false; + } + packet = std::move(*ids.d_packet); + } + else { + packet = std::move(query->query.d_buffer); + } + + auto wasOK = setAlternateName(packet, ids, std::string_view(alternateName, alternateNameSize), std::string_view(tag, tagSize), std::string_view(tagValue, tagValueSize), std::string_view(formerNameTagName, formerNameTagSize)); + if (!wasOK) { + return false; + } + + if (query->d_isResponse) { + query->d_isResponse = false; + } + query->query.d_buffer = std::move(packet); // resume as query return dnsdist::queueQueryResumptionEvent(std::move(query)); } +bool dnsdist_ffi_dnsquestion_set_alternate_name(dnsdist_ffi_dnsquestion_t* dnsQuestion, const char* alternateName, size_t alternateNameSize, const char* tag, size_t tagSize, const char* tagValue, size_t tagValueSize, const char* formerNameTagName, size_t formerNameTagSize) +{ + if (dnsQuestion == nullptr || dnsQuestion->dq == nullptr || alternateName == nullptr || alternateNameSize == 0) { + return false; + } + + auto& ids = dnsQuestion->dq->ids; + auto& packet = dnsQuestion->dq->getMutableData(); + return setAlternateName(packet, ids, std::string_view(alternateName, alternateNameSize), std::string_view(tag, tagSize), std::string_view(tagValue, tagValueSize), std::string_view(formerNameTagName, formerNameTagSize)); +} + bool dnsdist_ffi_drop_from_async(uint16_t asyncID, uint16_t queryID) { if (!dnsdist::g_asyncHolder) { diff -Nru dnsdist-1.9.10/dnsdist-lua-inspection-ffi.cc dnsdist-1.9.14/dnsdist-lua-inspection-ffi.cc --- dnsdist-1.9.10/dnsdist-lua-inspection-ffi.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-lua-inspection-ffi.cc 2026-04-22 16:47:03.000000000 +0000 @@ -66,9 +66,11 @@ void dnsdist_ffi_stat_node_get_full_name_raw(const dnsdist_ffi_stat_node_t* node, const char** name, size_t* nameSize) { - const auto& storage = node->node.fullname; - *name = storage.c_str(); - *nameSize = storage.size(); + if (node->fullname.empty()) { + node->fullname = node->node.fullname.toString(); + } + *name = node->fullname.c_str(); + *nameSize = node->fullname.size(); } unsigned int dnsdist_ffi_stat_node_get_children_count(const dnsdist_ffi_stat_node_t* node) diff -Nru dnsdist-1.9.10/dnsdist-lua-inspection.cc dnsdist-1.9.14/dnsdist-lua-inspection.cc --- dnsdist-1.9.10/dnsdist-lua-inspection.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-lua-inspection.cc 2026-04-22 16:47:03.000000000 +0000 @@ -825,7 +825,7 @@ [](const StatNode& sn) -> unsigned int { return sn.children.size(); } ); - luaCtx.registerMember("fullname", &StatNode::fullname); + luaCtx.registerMember(std::string("fullname"), [](const StatNode& node) -> std::string { return !node.fullname.empty() ? node.fullname.toString() : ""; }); luaCtx.registerMember("labelsCount", &StatNode::labelsCount); luaCtx.registerMember("servfails", &StatNode::Stat::servfails); luaCtx.registerMember("nxdomains", &StatNode::Stat::nxdomains); diff -Nru dnsdist-1.9.10/dnsdist-lua.cc dnsdist-1.9.14/dnsdist-lua.cc --- dnsdist-1.9.10/dnsdist-lua.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-lua.cc 2026-04-22 16:47:03.000000000 +0000 @@ -1129,6 +1129,7 @@ bool statsRequireAuthentication{true}; bool apiRequiresAuthentication{true}; bool dashboardRequiresAuthentication{true}; + bool allowCrossOriginRequests{false}; int maxConcurrentConnections = 0; if (getOptionalValue(vars, "password", password) > 0) { @@ -1169,6 +1170,10 @@ setWebserverDashboardRequiresAuthentication(dashboardRequiresAuthentication); } + if (getOptionalValue(vars, "allowCrossOriginRequests", allowCrossOriginRequests) > 0) { + setWebserverAllowCrossOriginRequests(allowCrossOriginRequests); + } + if (getOptionalIntegerValue("setWebserverConfig", vars, "maxConcurrentConnections", maxConcurrentConnections) > 0) { setWebserverMaxConcurrentConnections(maxConcurrentConnections); } @@ -2721,12 +2726,7 @@ { std::string valueStr; if (getOptionalValue(vars, "congestionControlAlgo", valueStr) > 0) { - if (dnsdist::doq::s_available_cc_algorithms.count(valueStr) > 0) { - frontend->d_quicheParams.d_ccAlgo = valueStr; - } - else { - warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOH3Local'", valueStr); - } + frontend->d_quicheParams.d_ccAlgo = std::move(valueStr); } } parseTLSConfig(frontend->d_quicheParams.d_tlsConfig, "addDOH3Local", vars); @@ -2796,12 +2796,7 @@ { std::string valueStr; if (getOptionalValue(vars, "congestionControlAlgo", valueStr) > 0) { - if (dnsdist::doq::s_available_cc_algorithms.count(valueStr) > 0) { - frontend->d_quicheParams.d_ccAlgo = std::move(valueStr); - } - else { - warnlog("Ignoring unknown value '%s' for 'congestionControlAlgo' on 'addDOQLocal'", valueStr); - } + frontend->d_quicheParams.d_ccAlgo = std::move(valueStr); } } parseTLSConfig(frontend->d_quicheParams.d_tlsConfig, "addDOQLocal", vars); @@ -3536,7 +3531,7 @@ setupLuaBindingsDNSQuestion(luaCtx); setupLuaBindingsKVS(luaCtx, client); setupLuaBindingsLogging(luaCtx); - setupLuaBindingsNetwork(luaCtx, client); + setupLuaBindingsNetwork(luaCtx, client, configCheck); setupLuaBindingsPacketCache(luaCtx, client); setupLuaBindingsProtoBuf(luaCtx, client, configCheck); setupLuaBindingsRings(luaCtx, client); diff -Nru dnsdist-1.9.10/dnsdist-lua.hh dnsdist-1.9.14/dnsdist-lua.hh --- dnsdist-1.9.10/dnsdist-lua.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-lua.hh 2026-04-22 16:47:03.000000000 +0000 @@ -170,7 +170,7 @@ void setupLuaBindingsDNSQuestion(LuaContext& luaCtx); void setupLuaBindingsKVS(LuaContext& luaCtx, bool client); void setupLuaBindingsLogging(LuaContext& luaCtx); -void setupLuaBindingsNetwork(LuaContext& luaCtx, bool client); +void setupLuaBindingsNetwork(LuaContext& luaCtx, bool client, bool configCheck); void setupLuaBindingsPacketCache(LuaContext& luaCtx, bool client); void setupLuaBindingsProtoBuf(LuaContext& luaCtx, bool client, bool configCheck); void setupLuaBindingsRings(LuaContext& luaCtx, bool client); diff -Nru dnsdist-1.9.10/dnsdist-nghttp2-in.cc dnsdist-1.9.14/dnsdist-nghttp2-in.cc --- dnsdist-1.9.10/dnsdist-nghttp2-in.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-nghttp2-in.cc 2026-04-22 16:47:03.000000000 +0000 @@ -288,15 +288,18 @@ void IncomingHTTP2Connection::handleConnectionReady() { - constexpr std::array settings{{{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100U}}}; + constexpr std::array settings{{{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, dnsdist::doh::MAX_INCOMING_CONCURRENT_STREAMS}}}; auto ret = nghttp2_submit_settings(d_session.get(), NGHTTP2_FLAG_NONE, settings.data(), settings.size()); if (ret != 0) { throw std::runtime_error("Fatal error: " + std::string(nghttp2_strerror(ret))); } d_needFlush = true; - ret = nghttp2_session_send(d_session.get()); - if (ret != 0) { - throw std::runtime_error("Fatal error: " + std::string(nghttp2_strerror(ret))); + + if (!d_inReadFunction) { + ret = nghttp2_session_send(d_session.get()); + if (ret != 0) { + throw std::runtime_error("Fatal error: " + std::string(nghttp2_strerror(ret))); + } } } @@ -440,6 +443,24 @@ if (nghttp2_session_want_read(d_session.get()) != 0) { updateIO(IOState::NeedRead, handleReadableIOCallback); } + else { + if (getConcurrentStreamsCount() == 0) { + d_connectionDied = true; + stopIO(); + } + else { + updateIO(IOState::Done, handleReadableIOCallback); + } + } + } + else { + if (getConcurrentStreamsCount() == 0) { + d_connectionDied = true; + stopIO(); + } + else { + updateIO(IOState::Done, handleReadableIOCallback); + } } } catch (const std::exception& e) { @@ -526,25 +547,48 @@ static const std::string s_schemeHeaderName(":scheme"); static const std::string s_xForwardedForHeaderName("x-forwarded-for"); +static void addHeader(std::vector& headers, const std::string_view& name, bool nameIsStatic, const std::string_view& value, bool valueIsStatic) +{ + /* Be careful when setting NGHTTP2_NV_FLAG_NO_COPY_NAME or NGHTTP2_NV_FLAG_NO_COPY_VALUE, the corresponding name or value needs to exist until after nghttp2_session_send() has been called, not just nghttp2_submit_response(). */ + uint8_t flag{NGHTTP2_NV_FLAG_NONE}; + if (nameIsStatic) { + flag |= NGHTTP2_NV_FLAG_NO_COPY_NAME; + } + if (valueIsStatic) { + flag |= NGHTTP2_NV_FLAG_NO_COPY_VALUE; + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast,cppcoreguidelines-pro-type-reinterpret-cast): nghttp2 API + headers.push_back({const_cast(reinterpret_cast(name.data())), const_cast(reinterpret_cast(value.data())), name.size(), value.size(), flag}); +} + void NGHTTP2Headers::addStaticHeader(std::vector& headers, NGHTTP2Headers::HeaderConstantIndexes nameKey, NGHTTP2Headers::HeaderConstantIndexes valueKey) { const auto& name = s_headerConstants.at(static_cast(nameKey)); const auto& value = s_headerConstants.at(static_cast(valueKey)); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast,cppcoreguidelines-pro-type-reinterpret-cast): nghttp2 API - headers.push_back({const_cast(reinterpret_cast(name.c_str())), const_cast(reinterpret_cast(value.c_str())), name.size(), value.size(), NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE}); + addHeader(headers, name, true, value, true); } void NGHTTP2Headers::addCustomDynamicHeader(std::vector& headers, const std::string& name, const std::string_view& value) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast,cppcoreguidelines-pro-type-reinterpret-cast): nghttp2 API - headers.push_back({const_cast(reinterpret_cast(name.data())), const_cast(reinterpret_cast(value.data())), name.size(), value.size(), NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE}); + addHeader(headers, name, false, value, false); } void NGHTTP2Headers::addDynamicHeader(std::vector& headers, NGHTTP2Headers::HeaderConstantIndexes nameKey, const std::string_view& value) { const auto& name = s_headerConstants.at(static_cast(nameKey)); - NGHTTP2Headers::addCustomDynamicHeader(headers, name, value); + addHeader(headers, name, true, value, false); +} + +std::unordered_map::iterator IncomingHTTP2Connection::getStreamContext(StreamID streamID) +{ + auto streamIt = d_currentStreams.find(streamID); + if (streamIt == d_currentStreams.end()) { + /* it might have been closed by the remote end in the meantime */ + d_killedStreams.erase(streamID); + } + return streamIt; } IOState IncomingHTTP2Connection::sendResponse(const struct timeval& now, TCPResponse&& response) @@ -552,7 +596,7 @@ if (response.d_idstate.d_streamID == -1) { throw std::runtime_error("Invalid DoH stream ID while sending response"); } - auto streamIt = d_currentStreams.find(response.d_idstate.d_streamID); + auto streamIt = getStreamContext(response.d_idstate.d_streamID); if (streamIt == d_currentStreams.end()) { /* it might have been closed by the remote end in the meantime */ return hasPendingWrite() ? IOState::NeedWrite : IOState::Done; @@ -592,7 +636,7 @@ throw std::runtime_error("Invalid DoH stream ID while handling I/O error notification"); } - auto streamIt = d_currentStreams.find(response.d_idstate.d_streamID); + auto streamIt = getStreamContext(response.d_idstate.d_streamID); if (streamIt == d_currentStreams.end()) { /* it might have been closed by the remote end in the meantime */ return; @@ -735,18 +779,21 @@ NGHTTP2Headers::addCustomDynamicHeader(headers, key, value); } + context.d_sendingResponse = true; auto ret = nghttp2_submit_response(d_session.get(), streamID, headers.data(), headers.size(), &data_provider); if (ret != 0) { - d_currentStreams.erase(streamID); vinfolog("Error submitting HTTP response for stream %d: %s", streamID, nghttp2_strerror(ret)); + d_currentStreams.erase(streamID); return false; } - ret = nghttp2_session_send(d_session.get()); - if (ret != 0) { - d_currentStreams.erase(streamID); - vinfolog("Error flushing HTTP response for stream %d: %s", streamID, nghttp2_strerror(ret)); - return false; + if (!d_inReadFunction) { + ret = nghttp2_session_send(d_session.get()); + if (ret != 0) { + d_currentStreams.erase(streamID); + vinfolog("Error flushing HTTP response for stream %d: %s", streamID, nghttp2_strerror(ret)); + return false; + } } return true; @@ -823,6 +870,16 @@ query.d_headers.reset(); } } + else if (!d_ci.cs->dohFrontend->d_earlyACLDrop) { + auto& holders = d_threadData.holders; + /* ONLY ACL lookup because the early check was skipped */ + if (!holders.acl->match(d_proxiedRemote)) { + ++dnsdist::metrics::g_stats.aclDrops; + vinfolog("Query from %s (%s) (DoH) dropped because of ACL", d_ci.remote.toStringWithPort(), d_proxiedRemote.toStringWithPort()); + handleImmediateResponse(403, "DoH query not allowed because of ACL"); + return; + } + } if (d_ci.cs->dohFrontend->d_exactPathMatching) { if (d_ci.cs->dohFrontend->d_urls.count(query.d_path) == 0) { @@ -921,7 +978,7 @@ /* is this the last frame for this stream? */ if ((frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_DATA) && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) != 0) { auto streamID = frame->hd.stream_id; - auto stream = conn->d_currentStreams.find(streamID); + auto stream = conn->getStreamContext(streamID); if (stream != conn->d_currentStreams.end()) { conn->handleIncomingQuery(std::move(stream->second), streamID); } @@ -941,7 +998,16 @@ { auto* conn = static_cast(user_data); - conn->d_currentStreams.erase(stream_id); + auto streamIt = conn->d_currentStreams.find(stream_id); + if (streamIt == conn->d_currentStreams.end()) { + return 0; + } + + if (!streamIt->second.d_sendingResponse) { + conn->d_killedStreams.emplace(stream_id); + } + + conn->d_currentStreams.erase(streamIt); return 0; } @@ -952,20 +1018,23 @@ } auto* conn = static_cast(user_data); + auto close_connection = [](IncomingHTTP2Connection* connection) -> int { + connection->d_connectionClosing = true; + connection->d_needFlush = true; + nghttp2_session_terminate_session(connection->d_session.get(), NGHTTP2_REFUSED_STREAM); + return 0; + }; + + if (conn->getConcurrentStreamsCount() >= dnsdist::doh::MAX_INCOMING_CONCURRENT_STREAMS) { + vinfolog("Too many concurrent streams on connection from %s", conn->d_ci.remote.toStringWithPort()); + return close_connection(conn); + } + auto insertPair = conn->d_currentStreams.emplace(frame->hd.stream_id, PendingQuery()); if (!insertPair.second) { /* there is a stream ID collision, something is very wrong! */ - vinfolog("Stream ID collision (%d) on connection from %d", frame->hd.stream_id, conn->d_ci.remote.toStringWithPort()); - conn->d_connectionClosing = true; - conn->d_needFlush = true; - nghttp2_session_terminate_session(conn->d_session.get(), NGHTTP2_NO_ERROR); - auto ret = nghttp2_session_send(conn->d_session.get()); - if (ret != 0) { - vinfolog("Error flushing HTTP response for stream %d from %s: %s", frame->hd.stream_id, conn->d_ci.remote.toStringWithPort(), nghttp2_strerror(ret)); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - return 0; + vinfolog("Stream ID collision (%d) on connection from %s", frame->hd.stream_id, conn->d_ci.remote.toStringWithPort()); + return close_connection(conn); } return 0; @@ -1002,7 +1071,7 @@ return nameLen == expected.size() && memcmp(name, expected.data(), expected.size()) == 0; }; - auto stream = conn->d_currentStreams.find(frame->hd.stream_id); + auto stream = conn->getStreamContext(frame->hd.stream_id); if (stream == conn->d_currentStreams.end()) { vinfolog("Unable to match the stream ID %d to a known one!", frame->hd.stream_id); return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1055,6 +1124,11 @@ if (!query.d_headers) { query.d_headers = std::make_unique(); } + if (query.d_headers->size() >= dnsdist::doh::MAX_INCOMING_HTTP_HEADERS) { + /* be nice but not too nice */ + vinfolog("Too many incoming DoH headers"); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): nghttp2 API query.d_headers->insert({std::string(reinterpret_cast(name), nameLen), std::string(valueView)}); } @@ -1065,7 +1139,7 @@ int IncomingHTTP2Connection::on_data_chunk_recv_callback(nghttp2_session* session, uint8_t flags, IncomingHTTP2Connection::StreamID stream_id, const uint8_t* data, size_t len, void* user_data) { auto* conn = static_cast(user_data); - auto stream = conn->d_currentStreams.find(stream_id); + auto stream = conn->getStreamContext(stream_id); if (stream == conn->d_currentStreams.end()) { vinfolog("Unable to match the stream ID %d to a known one!", stream_id); return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1089,11 +1163,6 @@ conn->d_connectionClosing = true; conn->d_needFlush = true; nghttp2_session_terminate_session(conn->d_session.get(), NGHTTP2_NO_ERROR); - auto ret = nghttp2_session_send(conn->d_session.get()); - if (ret != 0) { - vinfolog("Error flushing HTTP response on connection from %s: %s", conn->d_ci.remote.toStringWithPort(), nghttp2_strerror(ret)); - return NGHTTP2_ERR_CALLBACK_FAILURE; - } return 0; } @@ -1155,7 +1224,7 @@ uint32_t IncomingHTTP2Connection::getConcurrentStreamsCount() const { - return d_currentStreams.size(); + return d_currentStreams.size() + d_killedStreams.size(); } boost::optional IncomingHTTP2Connection::getIdleClientReadTTD(struct timeval now) const @@ -1208,6 +1277,9 @@ ttd = getClientWriteTTD(now); d_ioState->update(newState, callback, shared, ttd); } + else if (newState == IOState::Done) { + d_ioState->reset(); + } } void IncomingHTTP2Connection::handleIOError() @@ -1217,6 +1289,7 @@ d_outPos = 0; nghttp2_session_terminate_session(d_session.get(), NGHTTP2_PROTOCOL_ERROR); d_currentStreams.clear(); + d_killedStreams.clear(); stopIO(); } diff -Nru dnsdist-1.9.10/dnsdist-nghttp2-in.hh dnsdist-1.9.14/dnsdist-nghttp2-in.hh --- dnsdist-1.9.10/dnsdist-nghttp2-in.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-nghttp2-in.hh 2026-04-22 16:47:03.000000000 +0000 @@ -55,6 +55,7 @@ size_t d_queryPos{0}; uint32_t d_statusCode{0}; Method d_method{Method::Unknown}; + bool d_sendingResponse{false}; }; IncomingHTTP2Connection(ConnectionInfo&& connectionInfo, TCPClientThreadData& threadData, const struct timeval& now); @@ -86,6 +87,7 @@ std::unique_ptr getDOHUnit(uint32_t streamID) override; void stopIO(); + std::unordered_map::iterator getStreamContext(StreamID streamID); uint32_t getConcurrentStreamsCount() const; void updateIO(IOState newState, const FDMultiplexer::callbackfunc_t& callback); void handleIOError(); @@ -101,6 +103,7 @@ std::unique_ptr d_session{nullptr, nghttp2_session_del}; std::unordered_map d_currentStreams; + std::unordered_set d_killedStreams; PacketBuffer d_out; PacketBuffer d_in; size_t d_outPos{0}; diff -Nru dnsdist-1.9.10/dnsdist-nghttp2.cc dnsdist-1.9.14/dnsdist-nghttp2.cc --- dnsdist-1.9.10/dnsdist-nghttp2.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-nghttp2.cc 2026-04-22 16:47:03.000000000 +0000 @@ -49,6 +49,8 @@ class DoHConnectionToBackend : public ConnectionToBackend { public: + using StreamID = int32_t; + DoHConnectionToBackend(const std::shared_ptr& ds, std::unique_ptr& mplexer, const struct timeval& now, std::string&& proxyProtocolPayload); void handleTimeout(const struct timeval& now, bool write) override; @@ -75,10 +77,14 @@ } private: + /* how many bytes we are willing to keep in a buffer waiting for the socket to become writable + again, until we stop accepting new queries */ + static constexpr size_t s_maxBufferedBytes = 65536U; + static ssize_t send_callback(nghttp2_session* session, const uint8_t* data, size_t length, int flags, void* user_data); static int on_frame_recv_callback(nghttp2_session* session, const nghttp2_frame* frame, void* user_data); - static int on_data_chunk_recv_callback(nghttp2_session* session, uint8_t flags, int32_t stream_id, const uint8_t* data, size_t len, void* user_data); - static int on_stream_close_callback(nghttp2_session* session, int32_t stream_id, uint32_t error_code, void* user_data); + static int on_data_chunk_recv_callback(nghttp2_session* session, uint8_t flags, StreamID stream_id, const uint8_t* data, size_t len, void* user_data); + static int on_stream_close_callback(nghttp2_session* session, StreamID stream_id, uint32_t error_code, void* user_data); static int on_header_callback(nghttp2_session* session, const nghttp2_frame* frame, const uint8_t* name, size_t namelen, const uint8_t* value, size_t valuelen, uint8_t flags, void* user_data); static int on_error_callback(nghttp2_session* session, int lib_error_code, const char* msg, size_t len, void* user_data); static void handleReadableIOCallback(int fd, FDMultiplexer::funcparam_t& param); @@ -109,7 +115,7 @@ static const std::unordered_map s_constants; - std::unordered_map d_currentStreams; + std::unordered_map d_currentStreams; std::string d_proxyProtocolPayload; PacketBuffer d_out; PacketBuffer d_in; @@ -118,6 +124,7 @@ size_t d_inPos{0}; bool d_healthCheckQuery{false}; bool d_firstWrite{true}; + bool d_inIOCallback{false}; }; using DownstreamDoHConnectionsManager = DownstreamConnectionsManager; @@ -215,7 +222,7 @@ bool DoHConnectionToBackend::reachedMaxStreamID() const { const uint32_t maximumStreamID = (static_cast(1) << 31) - 1; - return d_highestStreamID == maximumStreamID; + return d_highestStreamID >= maximumStreamID; } bool DoHConnectionToBackend::reachedMaxConcurrentQueries() const @@ -224,6 +231,13 @@ if (nghttp2_session_get_remote_settings(d_session.get(), NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) <= getConcurrentStreamsCount()) { return true; } + + /* somehow we already have a lot of data queued that we have not been able to + write to the outgoing socket, do not accept new queries just yet */ + if (d_out.size() >= s_maxBufferedBytes) { + return true; + } + return false; } @@ -286,7 +300,15 @@ pending.d_query = std::move(query); pending.d_sender = std::move(sender); - uint32_t streamId = nghttp2_session_get_next_stream_id(d_session.get()); + uint32_t tentativeStreamId = nghttp2_session_get_next_stream_id(d_session.get()); + if (tentativeStreamId == static_cast(1 << 31)) { + /* running out of stream IDs */ + d_connectionDied = true; + nghttp2_session_terminate_session(d_session.get(), NGHTTP2_NO_ERROR); + throw std::runtime_error("No more stream IDs"); + } + + auto streamId = static_cast(tentativeStreamId); auto insertPair = d_currentStreams.insert({streamId, std::move(pending)}); if (!insertPair.second) { /* there is a stream ID collision, something is very wrong! */ @@ -300,7 +322,9 @@ nghttp2_data_provider data_provider; data_provider.source.ptr = this; - data_provider.read_callback = [](nghttp2_session* session, int32_t stream_id, uint8_t* buf, size_t length, uint32_t* data_flags, nghttp2_data_source* source, void* user_data) -> ssize_t { + data_provider.read_callback = [](nghttp2_session* session, StreamID stream_id, uint8_t* buf, size_t length, uint32_t* data_flags, nghttp2_data_source* source, void* user_data) -> ssize_t { + (void)session; + (void)source; auto* conn = static_cast(user_data); auto& request = conn->d_currentStreams.at(stream_id); size_t toCopy = 0; @@ -325,12 +349,14 @@ throw std::runtime_error("Error submitting HTTP request:" + std::string(nghttp2_strerror(newStreamId))); } - auto rv = nghttp2_session_send(d_session.get()); - if (rv != 0) { - d_connectionDied = true; - ++d_ds->tcpDiedSendingQuery; - d_currentStreams.erase(streamId); - throw std::runtime_error("Error in nghttp2_session_send:" + std::to_string(rv)); + if (!d_inIOCallback) { + auto rtv = nghttp2_session_send(d_session.get()); + if (rtv != 0) { + d_connectionDied = true; + ++d_ds->tcpDiedSendingQuery; + d_currentStreams.erase(streamId); + throw std::runtime_error("Error in nghttp2_session_send: " + std::to_string(rtv)); + } } d_highestStreamID = newStreamId; @@ -356,6 +382,11 @@ throw std::runtime_error("Unexpected socket descriptor " + std::to_string(fd) + " received in " + std::string(__PRETTY_FUNCTION__) + ", expected " + std::to_string(conn->getHandle())); } + if (conn->d_inIOCallback) { + return; + } + conn->d_inIOCallback = true; + dnsdist::tcp::HandlingIOGuard handlingIOGuard(conn->d_inIOCallback); IOStateGuard ioGuard(conn->d_ioState); do { conn->d_inPos = 0; @@ -626,7 +657,7 @@ return 0; } -int DoHConnectionToBackend::on_data_chunk_recv_callback(nghttp2_session* session, uint8_t flags, int32_t stream_id, const uint8_t* data, size_t len, void* user_data) +int DoHConnectionToBackend::on_data_chunk_recv_callback(nghttp2_session* session, uint8_t flags, StreamID stream_id, const uint8_t* data, size_t len, void* user_data) { DoHConnectionToBackend* conn = reinterpret_cast(user_data); // cerr<<"Got data of size "<(user_data); diff -Nru dnsdist-1.9.10/dnsdist-tcp-downstream.cc dnsdist-1.9.14/dnsdist-tcp-downstream.cc --- dnsdist-1.9.10/dnsdist-tcp-downstream.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-tcp-downstream.cc 2026-04-22 16:47:03.000000000 +0000 @@ -858,6 +858,18 @@ return done; } +bool TCPConnectionToBackend::reachedMaxStreamID() const +{ + /* TCP/DoT has only 2^16 usable identifiers, DoH has 2^32 */ + const uint32_t maximumStreamID = std::numeric_limits::max() - 1; + if (d_highestStreamID >= maximumStreamID) { + return true; + } + + /* pending queries will need IDs, so we need to take them into account as well */ + return (d_pendingQueries.size() >= (maximumStreamID - d_highestStreamID)); +} + void setTCPDownstreamMaxIdleConnectionsPerBackend(uint64_t max) { DownstreamTCPConnectionsManager::setMaxIdleConnectionsPerDownstream(max); diff -Nru dnsdist-1.9.10/dnsdist-tcp-downstream.hh dnsdist-1.9.14/dnsdist-tcp-downstream.hh --- dnsdist-1.9.10/dnsdist-tcp-downstream.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-tcp-downstream.hh 2026-04-22 16:47:03.000000000 +0000 @@ -237,12 +237,7 @@ return d_state == State::idle && d_pendingQueries.size() == 0 && d_pendingResponses.size() == 0; } - bool reachedMaxStreamID() const override - { - /* TCP/DoT has only 2^16 usable identifiers, DoH has 2^32 */ - const uint32_t maximumStreamID = std::numeric_limits::max() - 1; - return d_highestStreamID == maximumStreamID; - } + bool reachedMaxStreamID() const override; bool reachedMaxConcurrentQueries() const override { diff -Nru dnsdist-1.9.10/dnsdist-tcp.cc dnsdist-1.9.14/dnsdist-tcp.cc --- dnsdist-1.9.10/dnsdist-tcp.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-tcp.cc 2026-04-22 16:47:03.000000000 +0000 @@ -1088,26 +1088,6 @@ return false; } -class HandlingIOGuard -{ -public: - HandlingIOGuard(bool& handlingIO) : - d_handlingIO(handlingIO) - { - } - HandlingIOGuard(const HandlingIOGuard&) = delete; - HandlingIOGuard(HandlingIOGuard&&) = delete; - HandlingIOGuard& operator=(const HandlingIOGuard& rhs) = delete; - HandlingIOGuard& operator=(HandlingIOGuard&&) = delete; - ~HandlingIOGuard() - { - d_handlingIO = false; - } - -private: - bool& d_handlingIO; -}; - void IncomingTCPConnectionState::handleIO() { // let's make sure we are not already in handleIO() below in the stack: @@ -1119,7 +1099,7 @@ return; } d_handlingIO = true; - HandlingIOGuard reentryGuard(d_handlingIO); + dnsdist::tcp::HandlingIOGuard reentryGuard(d_handlingIO); // why do we loop? Because the TLS layer does buffering, and thus can have data ready to read // even though the underlying socket is not ready, so we need to actually ask for the data first diff -Nru dnsdist-1.9.10/dnsdist-tcp.hh dnsdist-1.9.14/dnsdist-tcp.hh --- dnsdist-1.9.10/dnsdist-tcp.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-tcp.hh 2026-04-22 16:47:03.000000000 +0000 @@ -305,6 +305,29 @@ const uint64_t d_maxthreads{0}; }; +namespace dnsdist::tcp +{ +class HandlingIOGuard +{ +public: + HandlingIOGuard(bool& handlingIO) : + d_handlingIO(handlingIO) + { + } + HandlingIOGuard(const HandlingIOGuard&) = delete; + HandlingIOGuard(HandlingIOGuard&&) = delete; + HandlingIOGuard& operator=(const HandlingIOGuard& rhs) = delete; + HandlingIOGuard& operator=(HandlingIOGuard&&) = delete; + ~HandlingIOGuard() + { + d_handlingIO = false; + } + +private: + bool& d_handlingIO; +}; +} + extern std::unique_ptr g_tcpclientthreads; std::unique_ptr getTCPCrossProtocolQueryFromDQ(DNSQuestion& dnsQuestion); diff -Nru dnsdist-1.9.10/dnsdist-web.cc dnsdist-1.9.14/dnsdist-web.cc --- dnsdist-1.9.10/dnsdist-web.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-web.cc 2026-04-22 16:47:03.000000000 +0000 @@ -57,6 +57,7 @@ bool apiRequiresAuthentication{true}; bool dashboardRequiresAuthentication{true}; bool statsRequireAuthentication{true}; + bool allowCrossOriginRequests{false}; // Whether the webserver / API allows cross-origin requests }; bool g_apiReadWrite{false}; @@ -391,10 +392,14 @@ resp.headers["Access-Control-Allow-Headers"] = "Authorization, X-API-Key"; } - resp.headers["Access-Control-Allow-Origin"] = origin->second; + const auto allowCrossOrigin = g_webserverConfig.lock()->allowCrossOriginRequests; + if (allowCrossOrigin) { + resp.headers["Access-Control-Allow-Origin"] = origin->second; + resp.headers["Vary"] = "Origin"; // prevents cached data to be used for a different Origin - if (isAStatsRequest(req) || isAnAPIRequestAllowedWithWebAuth(req)) { - resp.headers["Access-Control-Allow-Credentials"] = "true"; + if (isAStatsRequest(req) || isAnAPIRequestAllowedWithWebAuth(req)) { + resp.headers["Access-Control-Allow-Credentials"] = "true"; + } } } } @@ -1957,6 +1962,11 @@ g_webserverConfig.lock()->dashboardRequiresAuthentication = require; } +void setWebserverAllowCrossOriginRequests(bool allow) +{ + g_webserverConfig.lock()->allowCrossOriginRequests = allow; +} + void setWebserverMaxConcurrentConnections(size_t max) { s_connManager.setMaxConcurrentConnections(max); diff -Nru dnsdist-1.9.10/dnsdist-web.hh dnsdist-1.9.14/dnsdist-web.hh --- dnsdist-1.9.10/dnsdist-web.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist-web.hh 2026-04-22 16:47:03.000000000 +0000 @@ -11,6 +11,7 @@ void setWebserverDashboardRequiresAuthentication(bool); void setWebserverStatsRequireAuthentication(bool); void setWebserverMaxConcurrentConnections(size_t); +void setWebserverAllowCrossOriginRequests(bool); void dnsdistWebserverThread(int sock, const ComboAddress& local); diff -Nru dnsdist-1.9.10/dnsdist.1 dnsdist-1.9.14/dnsdist.1 --- dnsdist-1.9.10/dnsdist.1 2025-05-20 09:14:13.000000000 +0000 +++ dnsdist-1.9.14/dnsdist.1 2026-04-22 16:48:00.000000000 +0000 @@ -27,7 +27,7 @@ .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "DNSDIST" "1" "May 20, 2025" "" "dnsdist" +.TH "DNSDIST" "1" "Apr 22, 2026" "" "dnsdist" .SH NAME dnsdist \- A DNS and DoS aware, scriptable loadbalancer .SH SYNOPSIS diff -Nru dnsdist-1.9.10/dnsdist.cc dnsdist-1.9.14/dnsdist.cc --- dnsdist-1.9.10/dnsdist.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist.cc 2026-04-22 16:47:03.000000000 +0000 @@ -3185,9 +3185,9 @@ std::thread udpThreadHandle(udpClientThread, udpStates); udpThreadHandle.detach(); } - if (!tcpStates.empty()) { - g_tcpclientthreads = std::make_unique(1, tcpStates); - } + + /* Gives TCP client threads by default */ + g_tcpclientthreads = std::make_unique(1, tcpStates); #endif /* USE_SINGLE_ACCEPTOR_THREAD */ } } diff -Nru dnsdist-1.9.10/dnsdist.hh dnsdist-1.9.14/dnsdist.hh --- dnsdist-1.9.10/dnsdist.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist.hh 2026-04-22 16:47:03.000000000 +0000 @@ -87,6 +87,7 @@ } PacketBuffer& getMutableData() { + ednsOptions.reset(); return data; } diff -Nru dnsdist-1.9.10/dnsdist.service.in dnsdist-1.9.14/dnsdist.service.in --- dnsdist-1.9.10/dnsdist.service.in 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsdist.service.in 2026-04-22 16:47:03.000000000 +0000 @@ -20,7 +20,7 @@ # Tuning TasksMax=8192 -LimitNOFILE=16384 +LimitNOFILE=1000000 # Note: increasing the amount of lockable memory is required to use eBPF support # LimitMEMLOCK=infinity diff -Nru dnsdist-1.9.10/dnsparser.cc dnsdist-1.9.14/dnsparser.cc --- dnsdist-1.9.10/dnsparser.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsparser.cc 2026-04-22 16:47:03.000000000 +0000 @@ -1223,13 +1223,12 @@ uint32_t dnsttl = reader.get32BitInt(); uint16_t contentLength = reader.get16BitInt(); uint16_t pos = reader.getPosition(); + reader.skip(contentLength); bool done = visitor(section, dnsclass, dnstype, dnsttl, contentLength, &packet.at(pos)); if (done) { return true; } - - reader.skip(contentLength); } } catch (...) { diff -Nru dnsdist-1.9.10/dnsparser.hh dnsdist-1.9.14/dnsparser.hh --- dnsdist-1.9.10/dnsparser.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnsparser.hh 2026-04-22 16:47:03.000000000 +0000 @@ -176,6 +176,11 @@ void skip(uint16_t n) { + size_t stop = d_pos; + stop += n; + if (stop > d_content.size()) { + throw std::out_of_range("Attempt to skip bytes (" + std::to_string(n) + " starting at " + std::to_string(d_pos) + ") farther than the packet's end (" + std::to_string(d_content.size()) + ")"); + } d_pos += n; } diff -Nru dnsdist-1.9.10/dnswriter.cc dnsdist-1.9.14/dnswriter.cc --- dnsdist-1.9.10/dnswriter.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnswriter.cc 2026-04-22 16:47:03.000000000 +0000 @@ -40,9 +40,9 @@ */ - -template GenericDNSPacketWriter::GenericDNSPacketWriter(Container& content, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint8_t opcode) - : d_content(content), d_qname(qname), d_canonic(false), d_lowerCase(false) +template +GenericDNSPacketWriter::GenericDNSPacketWriter(Container& content, const DNSName& qname, uint16_t qtype, uint16_t qclass, uint8_t opcode) : + d_qname(qname), d_content(content) { d_content.clear(); dnsheader dnsheader; @@ -63,9 +63,10 @@ xfr16BitInt(qtype); xfr16BitInt(qclass); - d_truncatemarker=d_content.size(); - d_sor = 0; - d_rollbackmarker = 0; + if (d_content.size() > std::numeric_limits::max()) { + throw std::range_error("Trying to build a packet larger than " + std::to_string(std::numeric_limits::max()) + " bytes"); + } + d_truncatemarker = d_content.size(); } template dnsheader* GenericDNSPacketWriter::getHeader() @@ -78,11 +79,14 @@ { d_compress = compress; commit(); - d_rollbackmarker=d_content.size(); + if (d_content.size() > std::numeric_limits::max()) { + throw std::range_error("Trying to build a packet larger than " + std::to_string(std::numeric_limits::max()) + " bytes"); + } + d_rollbackmarker = d_content.size(); - if(compress && !name.isRoot() && d_qname==name) { // don't do the whole label compression thing if we *know* we can get away with "see question" - except when compressing the root - static unsigned char marker[2]={0xc0, 0x0c}; - d_content.insert(d_content.end(), (const char *) &marker[0], (const char *) &marker[2]); + if (compress && !name.isRoot() && d_qname==name) { // don't do the whole label compression thing if we *know* we can get away with "see question" - except when compressing the root + static const std::array marker{0xc0, 0x0c}; + d_content.insert(d_content.end(), reinterpret_cast(marker.begin()), reinterpret_cast(marker.end())); } else { xfrName(name, compress); @@ -92,7 +96,8 @@ xfr32BitInt(ttl); xfr16BitInt(0); // this will be the record size d_recordplace = place; - d_sor=d_content.size(); // this will remind us where to stuff the record size + + d_sor = d_content.size(); // this will remind us where to stuff the record size } template void GenericDNSPacketWriter::addOpt(const uint16_t udpsize, const uint16_t extRCode, const uint16_t ednsFlags, const optvect_t& options, const uint8_t version) @@ -480,13 +485,23 @@ template void GenericDNSPacketWriter::commit() { - if(!d_sor) + if (d_sor == 0) { return; + } + + if (d_sor < 2 || d_sor > d_content.size()) { + throw std::range_error("Invalid start of record when trying to build a packet: " + std::to_string(d_sor) + " / " + std::to_string(d_content.size())); + } + + if (d_content.size() > std::numeric_limits::max()) { + throw std::range_error("Trying to build a packet larger than " + std::to_string(std::numeric_limits::max()) + " bytes"); + } + uint16_t rlen = d_content.size() - d_sor; - d_content[d_sor-2]=rlen >> 8; - d_content[d_sor-1]=rlen & 0xff; - d_sor=0; - dnsheader* dh=reinterpret_cast( &*d_content.begin()); + d_content.at(d_sor-2) = rlen >> 8; + d_content.at(d_sor-1) = rlen & 0xff; + d_sor = 0; + auto* dh = reinterpret_cast( &*d_content.begin()); switch(d_recordplace) { case DNSResourceRecord::QUESTION: dh->qdcount = htons(ntohs(dh->qdcount) + 1); diff -Nru dnsdist-1.9.10/dnswriter.hh dnsdist-1.9.14/dnswriter.hh --- dnsdist-1.9.10/dnswriter.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/dnswriter.hh 2026-04-22 16:47:03.000000000 +0000 @@ -162,21 +162,19 @@ private: uint16_t lookupName(const DNSName& name, uint16_t* matchlen); - vector d_namepositions; - // We declare 1 uint_16 in the public section, these 3 align on a 8-byte boundary - uint16_t d_sor; - uint16_t d_rollbackmarker; // start of last complete packet, for rollback - Container& d_content; + std::vector d_namepositions; DNSName d_qname; - - uint16_t d_truncatemarker; // end of header, for truncate - DNSResourceRecord::Place d_recordplace; - bool d_canonic, d_lowerCase, d_compress{false}; + Container& d_content; + size_t d_sor{0}; + uint16_t d_rollbackmarker{0}; // start of last complete packet, for rollback + uint16_t d_truncatemarker{0}; // end of header, for truncate + DNSResourceRecord::Place d_recordplace{DNSResourceRecord::QUESTION}; + bool d_canonic{false}; + bool d_lowerCase{false}; + bool d_compress{false}; }; using DNSPacketWriter = GenericDNSPacketWriter>; -typedef vector > labelparts_t; -// bool labeltokUnescape(labelparts_t& parts, const DNSName& label); std::vector segmentDNSText(const string& text); // from dnslabeltext.rl diff -Nru dnsdist-1.9.10/doh.cc dnsdist-1.9.14/doh.cc --- dnsdist-1.9.10/doh.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/doh.cc 2026-04-22 16:47:03.000000000 +0000 @@ -313,6 +313,7 @@ struct timeval d_connectionStartTime{0, 0}; size_t d_nbQueries{0}; int d_desc{-1}; + uint8_t d_concurrentStreams{0}; }; static thread_local std::unordered_map t_conns; @@ -386,6 +387,17 @@ return reasonIt->second; } +static DOHConnection* getConnectionFromQuery(const h2o_req_t* req) +{ + h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn); + const int descriptor = h2o_socket_get_fd(sock); + if (descriptor == -1) { + /* this should not happen, but let's not crash on it */ + return nullptr; + } + return &t_conns.at(descriptor); +} + /* Always called from the main DoH thread */ static void handleResponse(DOHFrontend& dohFrontend, st_h2o_req_t* req, uint16_t statusCode, const PacketBuffer& response, const std::unordered_map& customResponseHeaders, const std::string& contentType, bool addContentType) { @@ -461,6 +473,10 @@ ++dohFrontend.d_errorresponses; } + + if (auto* conn = getConnectionFromQuery(req)) { + --conn->d_concurrentStreams; + } } static std::unique_ptr getDUFromIDS(InternalQueryState& ids) @@ -918,6 +934,8 @@ via a pipe */ static void doh_dispatch_query(DOHServerConfig* dsc, h2o_handler_t* self, h2o_req_t* req, PacketBuffer&& query, const ComboAddress& local, const ComboAddress& remote, std::string&& path) { + auto* conn = getConnectionFromQuery(req); + try { /* we only parse it there as a sanity check, we will parse it again later */ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) @@ -942,6 +960,12 @@ if (dsc->dohFrontend->d_keepIncomingHeaders) { dohUnit->headers = std::make_unique>(); + if (req->headers.size >= dnsdist::doh::MAX_INCOMING_HTTP_HEADERS) { + /* be nice but not too nice */ + vinfolog("Too many incoming DoH headers"); + h2o_send_error_400(req, "Bad Request", "The DNS query had too many HTTP headers", 0); + return; + } dohUnit->headers->reserve(req->headers.size); for (size_t i = 0; i < req->headers.size; ++i) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): h2o API @@ -949,6 +973,9 @@ } } + if (conn != nullptr) { + ++conn->d_concurrentStreams; + } #ifdef HAVE_H2O_SOCKET_GET_SSL_SERVER_NAME h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn); const char * sni = h2o_socket_get_ssl_server_name(sock); @@ -966,17 +993,26 @@ if (!dsc->d_querySender.send(std::move(dohUnit))) { ++dnsdist::metrics::g_stats.dohQueryPipeFull; vinfolog("Unable to pass a DoH query to the DoH worker thread because the pipe is full"); + if (conn != nullptr) { + --conn->d_concurrentStreams; + } h2o_send_error_500(req, "Internal Server Error", "Internal Server Error", 0); } } catch (...) { vinfolog("Unable to pass a DoH query to the DoH worker thread because we couldn't write to the pipe: %s", stringerror()); + if (conn != nullptr) { + --conn->d_concurrentStreams; + } h2o_send_error_500(req, "Internal Server Error", "Internal Server Error", 0); } #endif /* USE_SINGLE_ACCEPTOR_THREAD */ } catch (const std::exception& e) { vinfolog("Had error parsing DoH DNS packet from %s: %s", remote.toStringWithPort(), e.what()); + if (conn != nullptr) { + --conn->d_concurrentStreams; + } h2o_send_error_400(req, "Bad Request", "The DNS query could not be parsed", 0); } } @@ -1046,15 +1082,19 @@ } // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): h2o API auto* dsc = static_cast(req->conn->ctx->storage.entries[0].data); - h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn); - - const int descriptor = h2o_socket_get_fd(sock); - if (descriptor == -1) { + auto* connPtr = getConnectionFromQuery(req); + if (connPtr == nullptr) { + return 0; + } + auto& conn = *connPtr; + if (conn.d_concurrentStreams >= dnsdist::doh::MAX_INCOMING_CONCURRENT_STREAMS) { + vinfolog("Too many concurrent streams on connection from %d", conn.d_remote.toStringWithPort()); return 0; } - auto& conn = t_conns.at(descriptor); ++conn.d_nbQueries; + + h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn); if (conn.d_nbQueries == 1) { if (h2o_socket_get_ssl_session_reused(sock) == 0) { ++dsc->clientState->tlsNewSessions; @@ -1121,6 +1161,7 @@ for (const auto& entry : *responsesMap) { if (entry->matches(path)) { const auto& customHeaders = entry->getHeaders(); + ++conn.d_concurrentStreams; handleResponse(*dsc->dohFrontend, req, entry->getStatusCode(), entry->getContent(), customHeaders ? *customHeaders : dsc->dohFrontend->d_customResponseHeaders, std::string(), false); return 0; } diff -Nru dnsdist-1.9.10/doh3.cc dnsdist-1.9.14/doh3.cc --- dnsdist-1.9.10/doh3.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/doh3.cc 2026-04-22 16:47:03.000000000 +0000 @@ -30,13 +30,12 @@ #include "misc.hh" #include "sstuff.hh" #include "threadname.hh" -#include "base64.hh" +#include "dnsdist-concurrent-connections.hh" #include "dnsdist-dnsparser.hh" #include "dnsdist-ecs.hh" #include "dnsdist-proxy-protocol.hh" #include "dnsdist-tcp.hh" -#include "dnsdist-random.hh" #include "doq-common.hh" @@ -62,7 +61,18 @@ H3Connection(H3Connection&&) = default; H3Connection& operator=(const H3Connection&) = delete; H3Connection& operator=(H3Connection&&) = default; - ~H3Connection() = default; + ~H3Connection() + { + try { + /* do not account if we have been moved! */ + if (d_conn) { + dnsdist::IncomingConcurrentTCPConnectionsManager::accountClosedTCPConnection(d_peer); + } + } + catch (...) { + /* in theory it might raise an exception, and we cannot allow it to be uncaught in a dtor */ + } + } ComboAddress d_peer; ComboAddress d_localAddr; @@ -620,10 +630,13 @@ if (downstream->passCrossProtocolQuery(std::move(cpq))) { return; } - // NOLINTNEXTLINE(bugprone-use-after-move): it was only moved if the call succeeded - unit = cpq->releaseDU(); - unit->status_code = 500; - handleImmediateResponse(std::move(unit), "DoH3 internal error"); + + /* On exceptional cases, cpq is moved but returns false above. So we check to make sure. See https://github.com/PowerDNS/pdns/issues/17109 */ + if (cpq) { + unit = cpq->releaseDU(); + unit->status_code = 500; + handleImmediateResponse(std::move(unit), "DoH3 internal error"); + } return; } catch (const std::exception& e) { @@ -699,6 +712,8 @@ ++clientState.nonCompliantQueries; ++frontend.d_errorResponses; h3_send_response(conn, streamID, 400, msg); + conn.d_streamBuffers.erase(streamID); + conn.d_headersBuffers.erase(streamID); }; auto& headers = conn.d_headersBuffers.at(streamID); @@ -712,6 +727,10 @@ std::string_view content(reinterpret_cast(value), value_len); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): Quiche API auto* headersptr = reinterpret_cast(argp); + if (headersptr->size() >= dnsdist::doh::MAX_INCOMING_HTTP_HEADERS) { + /* be nice but not too nice */ + return 1; + } headersptr->emplace(key, content); return 0; }, @@ -798,6 +817,13 @@ break; } + if (len > std::numeric_limits::max() || (std::numeric_limits::max() - streamBuffer.size()) < static_cast(len)) { + vinfolog("DOH3 data frame of size %d is too large for a DNS query (we already have %d)", len, streamBuffer.size()); + conn.d_streamBuffers.erase(streamID); + handleImmediateError("DoH3 non-compliant query"); + return; + } + buffer.resize(static_cast(len)); streamBuffer.insert(streamBuffer.end(), buffer.begin(), buffer.end()); } @@ -830,25 +856,28 @@ if (streamID < 0) { break; } + std::unique_ptr eventPtr(event, quiche_h3_event_free); + event = nullptr; conn.d_headersBuffers.try_emplace(streamID, h3_headers_t{}); - switch (quiche_h3_event_type(event)) { + switch (quiche_h3_event_type(eventPtr.get())) { case QUICHE_H3_EVENT_HEADERS: { - processH3HeaderEvent(clientState, frontend, conn, client, serverConnID, streamID, event); + processH3HeaderEvent(clientState, frontend, conn, client, serverConnID, streamID, eventPtr.get()); break; } case QUICHE_H3_EVENT_DATA: { - processH3DataEvent(clientState, frontend, conn, client, serverConnID, streamID, event, buffer); + processH3DataEvent(clientState, frontend, conn, client, serverConnID, streamID, eventPtr.get(), buffer); break; } case QUICHE_H3_EVENT_FINISHED: case QUICHE_H3_EVENT_RESET: - case QUICHE_H3_EVENT_PRIORITY_UPDATE: + conn.d_headersBuffers.erase(streamID); + conn.d_streamBuffers.erase(streamID); + break; case QUICHE_H3_EVENT_GOAWAY: + case QUICHE_H3_EVENT_PRIORITY_UPDATE: break; } - - quiche_h3_event_free(event); } } @@ -931,6 +960,11 @@ continue; } + if (!dnsdist::IncomingConcurrentTCPConnectionsManager::accountNewTCPConnection(client)) { + DEBUGLOG("Connection not allowed!"); + continue; + } + DEBUGLOG("Creating a new connection"); conn = createConnection(*frontend.d_server_config, serverConnID, *originalDestinationID, localAddr, client); if (!conn) { diff -Nru dnsdist-1.9.10/doq-common.cc dnsdist-1.9.14/doq-common.cc --- dnsdist-1.9.10/doq-common.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/doq-common.cc 2026-04-22 16:47:03.000000000 +0000 @@ -280,10 +280,7 @@ quiche_config_log_keys(config.get()); } - auto algo = dnsdist::doq::s_available_cc_algorithms.find(params.d_ccAlgo); - if (algo != dnsdist::doq::s_available_cc_algorithms.end()) { - quiche_config_set_cc_algorithm(config.get(), static_cast(algo->second)); - } + quiche_config_set_cc_algorithm_name(config.get(), params.d_ccAlgo.c_str()); { PacketBuffer resetToken; diff -Nru dnsdist-1.9.10/doq-common.hh dnsdist-1.9.14/doq-common.hh --- dnsdist-1.9.10/doq-common.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/doq-common.hh 2026-04-22 16:47:03.000000000 +0000 @@ -38,13 +38,6 @@ namespace dnsdist::doq { - -static const std::map s_available_cc_algorithms = { - {"reno", QUICHE_CC_RENO}, - {"cubic", QUICHE_CC_CUBIC}, - {"bbr", QUICHE_CC_BBR}, -}; - using QuicheConnection = std::unique_ptr; using QuicheHTTP3Connection = std::unique_ptr; using QuicheConfig = std::shared_ptr; diff -Nru dnsdist-1.9.10/doq.cc dnsdist-1.9.14/doq.cc --- dnsdist-1.9.10/doq.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/doq.cc 2026-04-22 16:47:03.000000000 +0000 @@ -31,11 +31,11 @@ #include "sstuff.hh" #include "threadname.hh" +#include "dnsdist-concurrent-connections.hh" #include "dnsdist-dnsparser.hh" #include "dnsdist-ecs.hh" #include "dnsdist-proxy-protocol.hh" #include "dnsdist-tcp.hh" -#include "dnsdist-random.hh" #include "doq-common.hh" @@ -59,7 +59,18 @@ Connection(Connection&&) = default; Connection& operator=(const Connection&) = delete; Connection& operator=(Connection&&) = default; - ~Connection() = default; + ~Connection() + { + try { + /* do not account if we have been moved! */ + if (d_conn) { + dnsdist::IncomingConcurrentTCPConnectionsManager::accountClosedTCPConnection(d_peer); + } + } + catch (...) { + /* in theory it might raise an exception, and we cannot allow it to be uncaught in a dtor */ + } + } ComboAddress d_peer; ComboAddress d_localAddr; @@ -535,9 +546,11 @@ if (downstream->passCrossProtocolQuery(std::move(cpq))) { return; } - // NOLINTNEXTLINE(bugprone-use-after-move): it was only moved if the call succeeded - unit = cpq->releaseDU(); - handleImmediateResponse(std::move(unit), "DoQ internal error"); + /* On exceptional cases, cpq is moved but returns false above. So we check to make sure. See https://github.com/PowerDNS/pdns/issues/17109 */ + if (cpq) { + unit = cpq->releaseDU(); + handleImmediateResponse(std::move(unit), "DoQ internal error"); + } return; } catch (const std::exception& e) { @@ -630,6 +643,16 @@ ++dnsdist::metrics::g_stats.nonCompliantQueries; ++clientState.nonCompliantQueries; quiche_conn_stream_shutdown(conn.d_conn.get(), streamID, QUICHE_SHUTDOWN_WRITE, static_cast(DOQ_Error_Codes::DOQ_PROTOCOL_ERROR)); + conn.d_streamBuffers.erase(streamID); + return; + } + + if (received > std::numeric_limits::max() || (std::numeric_limits::max() - streamBuffer.size()) < static_cast(received)) { + vinfolog("DoQ data frame of size %d is too large for a DNS query (we already have %d)", received, streamBuffer.size()); + conn.d_streamBuffers.erase(streamID); + ++dnsdist::metrics::g_stats.nonCompliantQueries; + ++clientState.nonCompliantQueries; + quiche_conn_stream_shutdown(conn.d_conn.get(), streamID, QUICHE_SHUTDOWN_WRITE, static_cast(DOQ_Error_Codes::DOQ_PROTOCOL_ERROR)); return; } @@ -643,6 +666,7 @@ ++dnsdist::metrics::g_stats.nonCompliantQueries; ++clientState.nonCompliantQueries; quiche_conn_stream_shutdown(conn.d_conn.get(), streamID, QUICHE_SHUTDOWN_WRITE, static_cast(DOQ_Error_Codes::DOQ_PROTOCOL_ERROR)); + conn.d_streamBuffers.erase(streamID); return; } @@ -652,6 +676,7 @@ ++dnsdist::metrics::g_stats.nonCompliantQueries; ++clientState.nonCompliantQueries; quiche_conn_stream_shutdown(conn.d_conn.get(), streamID, QUICHE_SHUTDOWN_WRITE, static_cast(DOQ_Error_Codes::DOQ_PROTOCOL_ERROR)); + conn.d_streamBuffers.erase(streamID); return; } DEBUGLOG("Dispatching query"); @@ -737,6 +762,11 @@ continue; } + if (!dnsdist::IncomingConcurrentTCPConnectionsManager::accountNewTCPConnection(client)) { + DEBUGLOG("Connection not allowed!"); + continue; + } + DEBUGLOG("Creating a new connection"); conn = createConnection(*frontend.d_server_config, serverConnID, *originalDestinationID, client, localAddr); if (!conn) { diff -Nru dnsdist-1.9.10/ext/luawrapper/include/LuaContext.hpp dnsdist-1.9.14/ext/luawrapper/include/LuaContext.hpp --- dnsdist-1.9.10/ext/luawrapper/include/LuaContext.hpp 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/ext/luawrapper/include/LuaContext.hpp 2026-04-22 16:47:02.000000000 +0000 @@ -1449,21 +1449,26 @@ // if pcall failed, analyzing the problem and throwing if (pcallReturnValue != 0) { + switch (pcallReturnValue) { + case LUA_ERRMEM: + throw std::bad_alloc{}; + case LUA_ERRERR: + throw ExecutionErrorException("while handling a Lua error, the traceback handler failed (LUA_ERRERR)"); + // all other values should have left us an error and a traceback + } + - // stack top: {error, traceback} - lua_rawgeti(state, -1, 1); // stack top: {error, traceback}, error - lua_rawgeti(state, -2, 2); // stack top: {error, traceback}, error, traceback - lua_remove(state, -3); // stack top: error, traceback - - PushedObject traceBackRef{state, 1}; - const auto traceBack = readTopAndPop(state, std::move(traceBackRef)); // stack top: error - PushedObject errorCode{state, 1}; - - // an error occurred during execution, either an error message or a std::exception_ptr was pushed on the stack - if (pcallReturnValue == LUA_ERRMEM) { - throw std::bad_alloc{}; + if (lua_gettop(state) >= 1 && lua_type(state, -1) == LUA_TTABLE) { + // stack top: {error, traceback} + lua_rawgeti(state, -1, 1); // stack top: {error, traceback}, error + lua_rawgeti(state, -2, 2); // stack top: {error, traceback}, error, traceback + lua_remove(state, -3); // stack top: error, traceback + + PushedObject traceBackRef{state, 1}; + const auto traceBack = readTopAndPop(state, std::move(traceBackRef)); // stack top: error + PushedObject errorCode{state, 1}; - } else if (pcallReturnValue == LUA_ERRRUN) { + // an error occurred during execution, either an error message or a std::exception_ptr was pushed on the stack if (lua_isstring(state, 1)) { // the error is a string const auto str = readTopAndPop(state, std::move(errorCode)); @@ -1484,6 +1489,9 @@ throw ExecutionErrorException{"Unknown Lua error"}; } } + else { + throw ExecutionErrorException("while handling a Lua error, the traceback handler did not return a table (" + std::to_string(pcallReturnValue) + ")"); + } } return PushedObject{state, outArguments}; diff -Nru dnsdist-1.9.10/ext/yahttp/yahttp/reqresp.cpp dnsdist-1.9.14/ext/yahttp/yahttp/reqresp.cpp --- dnsdist-1.9.10/ext/yahttp/yahttp/reqresp.cpp 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/ext/yahttp/yahttp/reqresp.cpp 2026-04-22 16:47:02.000000000 +0000 @@ -40,7 +40,19 @@ } template - bool AsyncLoader::feed(const std::string& somedata) { + bool AsyncLoader::feed(const std::string& somedata) + { + if (state < 2) { + headersize += somedata.length(); // maye include some body data, we don't know yet... + if (headersize > target->max_header_size) { + if (target->kind == YAHTTP_TYPE_REQUEST) { + throw ParseError("Request header too large"); + } + else { + throw ParseError("Response header too large"); + } + } + } buffer.append(somedata); while(state < 2) { int cr=0; @@ -155,8 +167,8 @@ maxbody = minbody; } if (minbody < 1) return true; // guess there isn't anything left. - if (target->kind == YAHTTP_TYPE_REQUEST && static_cast(minbody) > target->max_request_size) throw ParseError("Max request body size exceeded"); - else if (target->kind == YAHTTP_TYPE_RESPONSE && static_cast(minbody) > target->max_response_size) throw ParseError("Max response body size exceeded"); + if (target->kind == YAHTTP_TYPE_REQUEST && minbody > target->max_request_size) throw ParseError("Max request body size exceeded"); + else if (target->kind == YAHTTP_TYPE_RESPONSE && minbody > target->max_response_size) throw ParseError("Max response body size exceeded"); } if (maxbody == 0) hasBody = false; @@ -175,20 +187,23 @@ buffer.copy(buf, pos); buf[pos]=0; // just in case... buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer - if (sscanf(buf, "%x", &chunk_size) != 1) { + if (sscanf(buf, "%zx", &chunk_size) != 1) { throw ParseError("Unable to parse chunk size"); } if (chunk_size == 0) { state = 3; break; } // last chunk - if (chunk_size > (std::numeric_limits::max() - 2)) { + if (chunk_size > (std::numeric_limits::max() - 2) || chunk_size > maxbody) { throw ParseError("Chunk is too large"); } } else { int crlf=1; - if (buffer.size() < static_cast(chunk_size+1)) return false; // expect newline + if (buffer.size() < chunk_size+1) return false; // expect newline if (buffer.at(chunk_size) == '\r') { - if (buffer.size() < static_cast(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return + if (buffer.size() < 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; + if (bodybuf.str().length() + chunk_size > maxbody) { + throw ParseError("Chunked body is too large"); + } std::string tmp = buffer.substr(0, chunk_size); buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf); bodybuf << tmp; diff -Nru dnsdist-1.9.10/ext/yahttp/yahttp/reqresp.hpp dnsdist-1.9.14/ext/yahttp/yahttp/reqresp.hpp --- dnsdist-1.9.10/ext/yahttp/yahttp/reqresp.hpp 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/ext/yahttp/yahttp/reqresp.hpp 2026-04-22 16:47:02.000000000 +0000 @@ -20,6 +20,10 @@ #include +#ifndef YAHTTP_MAX_HEADER_SIZE +#define YAHTTP_MAX_HEADER_SIZE (100 * 1024) +#endif + #ifndef YAHTTP_MAX_REQUEST_SIZE #define YAHTTP_MAX_REQUEST_SIZE 2097152 #endif @@ -107,6 +111,7 @@ #endif max_request_size = YAHTTP_MAX_REQUEST_SIZE; max_response_size = YAHTTP_MAX_RESPONSE_SIZE; + max_header_size = YAHTTP_MAX_HEADER_SIZE; url = ""; method = ""; statusText = ""; @@ -129,6 +134,7 @@ 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; this->version = rhs.version; + this->max_header_size = rhs.max_header_size; #ifdef HAVE_CPP_FUNC_PTR this->renderer = rhs.renderer; #endif @@ -142,6 +148,7 @@ 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; this->version = rhs.version; + this->max_header_size = rhs.max_header_size; #ifdef HAVE_CPP_FUNC_PTR this->renderer = rhs.renderer; #endif @@ -165,8 +172,9 @@ std::string body; // renderer; //target = target_; hasBody = false; buffer = ""; + headersize = 0; this->target->initialize(); }; //NumberDomainType"; - var num=0; - var total=0, rest=0; - $.each(data["entries"], function(a,b) { - total+=b[0]; - if(num++ > 10) { - rest+=b[0]; - return; - } - if(b[1].length > 25) - b[1]=b[1].substring(0,25); - - bouw=bouw+(""+b[0]+""+b[1]+""+b[2]+""); - }); - bouw+=""+rest+"Rest"; - bouw=bouw+""; - $("#queryring").html(bouw); - - }); - - filtered=$("#filter2").is(':checked') - qstring='jsonstat?command=get-query-ring&name=servfail-queries'; - if(filtered) - qstring=qstring+"&public-filtered=1"; - - $.getJSON(qstring, - function(data) { - var bouw=""; - var num=0, total=0, rest=0; - $.each(data["entries"], function(a,b) { - total+=b[0]; - if(num++ > 10) { - rest+=b[0]; - return; - } - if(b[1].length > 25) - b[1]=b[1].substring(0,25); - bouw=bouw+(""); - }); - bouw+=""; - bouw=bouw+"
NumberServfail domainType
"+b[0]+""+b[1]+""+b[2]+"
"+rest+"Rest
"; - $("#servfailqueryring").html(bouw); - - }); - - $.getJSON('jsonstat?command=get-remote-ring&name=remotes', - function(data) { - var bouw=""; - var num=0, total=0, rest=0; - $.each(data["entries"], function(a,b) { - total+=b[0]; - if(num++ > 10) { - rest +=b[0]; - return; - } - bouw=bouw+(""); - }); - bouw+=""; - bouw=bouw+"
NumberRemote
"+b[0]+""+b[1]+"
"+rest+"Rest
"; - $("#remotering").html(bouw); - - }); - - $.getJSON('jsonstat?command=get-remote-ring&name=servfail-remotes', - function(data) { - var bouw=""; - var num=0, total=0, rest=0; - $.each(data["entries"], function(a,b) { - total+=b[0]; - if(num++ > 10) { - rest += b[0]; - return; - } - bouw=bouw+(""); - }); - bouw+=""; - bouw=bouw+"
NumberServfail Remote
"+b[0]+""+b[1]+"
"+rest+"Rest
"; - $("#servfailremotering").html(bouw); - }); + var cell = $(''); + if (align) { + cell = $(''); + } + cell.text(untrusted); + row.append(cell); } function update() @@ -155,125 +74,147 @@ $("#latency-dot").text((data["latency-dot-avg10000"]/1000.0).toFixed(2)); $("#latency-doh").text((data["latency-doh-avg10000"]/1000.0).toFixed(2)); $("#latency-doq").text((data["latency-doq-avg10000"]/1000.0).toFixed(2)); - if(!gdata["cpu-sys-msec"]) - gdata=data; + if (!gdata["cpu-sys-msec"]) { + gdata = data; + } - var cpu=((1.0*data["cpu-sys-msec"]+1.0*data["cpu-user-msec"] - 1.0*gdata["cpu-sys-msec"]-1.0*gdata["cpu-user-msec"])/10.0); + var cpu = ((1*data["cpu-sys-msec"] + 1*data["cpu-user-msec"]) - (1*gdata["cpu-sys-msec"] + 1*gdata["cpu-user-msec"]))/10.0; $("#cpu").text(cpu.toFixed(2)); - var qps=1.0*data["queries"]-1.0*gdata["queries"]; + var qps = 1.0*data["queries"]-1.0*gdata["queries"]; $("#qps").text(qps.toFixed(2)); $("#server-policy").text(data["server-policy"]); - var servfailps=1.0*data["servfail-responses"]-1.0*gdata["servfail-responses"]; + var servfailps = (1*data["servfail-responses"]) - (1*gdata["servfail-responses"]); - var totpcache=1.0*data["cache-hits"]-1.0*gdata["cache-hits"]+1.0*data["cache-misses"]-1.0*gdata["cache-misses"]; - var hitrate=0; - if(totpcache > 0) { - hitrate=100.0*(data["cache-hits"]-1.0*gdata["cache-hits"])/totpcache; + var totpcache = (1*data["cache-hits"] + 1*data["cache-misses"]) - (1*gdata["cache-hits"] + 1*gdata["cache-misses"]); + var hitrate = 0; + if (totpcache > 0) { + hitrate = 100.0*(data["cache-hits"]-1.0*gdata["cache-hits"])/totpcache; $("#phitrate").text(hitrate.toFixed(2)); } - else + else { $("#phitrate").text(0); - + } + qpsgraph.series.addData({ qps: qps, servfailps: servfailps}); qpsgraph.render(); cpugraph.series.addData({ one: cpu, two: hitrate}); cpugraph.render(); - gdata=data; + gdata = data; }, error: function() { }, }); - + $.ajax({ url: 'api/v1/servers/localhost', type: 'GET', dataType: 'json', jsonp: false, success: function(data) { $("#version").text(data["daemon_type"]+" "+data["version"]); $("#acl").text(data["acl"]); $("#local").text(data["local"]); - var bouw=''; + var tableElement = $('
#NameAddressStatusUDP LatencyTCP LatencyQueriesDropsQPSOutWeightOrderPools
#NameAddressStatusUDP LatencyTCP LatencyQueriesDropsQPSOutWeightOrderPools
'); $.each(data["servers"], function(a,b) { - bouw = bouw + (""+b["id"]+""+b["name"]+""+b["address"]+""+b["state"]+""); + var row = $(''); var latency = (b["latency"] === null || b["latency"] === 0.0) ? "-" : b["latency"].toFixed(2); var tcpLatency = (b["tcpLatency"] === null || b["tcpLatency"] === 0.0) ? "-" : b["tcpLatency"].toFixed(2); - bouw = bouw + (""+latency+""+tcpLatency+""+b["queries"]+""+b["reuseds"]+""+(b["qps"]).toFixed(2)+""+b["outstanding"]+""); - bouw = bouw + (""+b["weight"]+""+b["order"]+""+b["pools"]+""); - }); - bouw = bouw + ""; - $("#downstreams").html(bouw); - - bouw=''; - if(data["rules"].length) { + appendCellToRow(row, b["id"]); + appendCellToRow(row, b["name"], 'left'); + appendCellToRow(row, b["address"], 'left'); + appendCellToRow(row, b["state"]); + appendCellToRow(row, latency); + appendCellToRow(row, tcpLatency); + appendCellToRow(row, b["queries"]); + appendCellToRow(row, b["reused"]); + appendCellToRow(row, b["qps"].toFixed(2)); + appendCellToRow(row, b["outstanding"]); + appendCellToRow(row, b["weight"]); + appendCellToRow(row, b["order"]); + appendCellToRow(row, b["pools"], 'left'); + tableElement.append(row); + }); + $("#downstreams").html(tableElement); + + tableElement = $('
#NameRuleActionMatches
#NameRuleActionMatches
'); + if (data["rules"].length) { $.each(data["rules"], function(a,b) { - bouw = bouw + (""+b["id"]+""+b["name"]+""+b["rule"]+""+b["action"]+""); - bouw = bouw + (""+b["matches"]+""); - }); + var row = $(''); + appendCellToRow(row, b["id"]); + appendCellToRow(row, b["name"], 'left'); + appendCellToRow(row, b["rule"], 'left'); + appendCellToRow(row, b["action"]); + appendCellToRow(row, b["matches"]); + tableElement.append(row); + }); } - else - bouw = bouw + 'No rules defined'; - bouw = bouw + ""; - $("#rules").html(bouw); + else { + tableElement.append($('No rules defined')); + } + $("#rules").html(tableElement); - bouw=''; - if(data["response-rules"].length) { + tableElement = $('
#NameResponse RuleActionMatches
#NameResponse RuleActionMatches
'); + if (data["response-rules"].length) { $.each(data["response-rules"], function(a,b) { - bouw = bouw + (""+b["id"]+""+b["name"]+""+b["rule"]+""+b["action"]+""); - bouw = bouw + (""+b["matches"]+""); + var row = $(''); + appendCellToRow(row, b["id"]); + appendCellToRow(row, b["name"], 'left'); + appendCellToRow(row, b["rule"], 'left'); + appendCellToRow(row, b["action"]); + appendCellToRow(row, b["matches"]); + tableElement.append(row); }); } - else - bouw = bouw + 'No response rules defined'; - bouw = bouw + ""; - $("#response-rules").html(bouw); + else { + tableElement.append($('No response rules defined')); + } + $("#response-rules").html(tableElement); } }); - -// if((intervalcount++)%5) - // return; - // updateRingBuffers(); - $.ajax({ url: 'jsonstat?command=dynblocklist', type: 'GET', dataType: 'json', jsonp: false, success: function(data) { - var bouw=''; - var gotsome=false; + var tableElement = $('
Dyn blocked netmaskSecondsBlockseBPFReason
Dyn blocked netmaskSecondsBlockseBPFReason
'); + var gotsome = false; $.each(data, function(a,b) { - bouw=bouw+(""+a+""+b.seconds+""+b.blocks+""+b.ebpf+""+b.reason+""); - gotsome=true; + var row = $(''); + appendCellToRow(row, a); + appendCellToRow(row, b.seconds); + appendCellToRow(row, b.blocks); + appendCellToRow(row, b.ebpf); + appendCellToRow(row, b.reason); + tableElement.append(row); + gotsome = true; }); - - if(!gotsome) - bouw = bouw + 'No dynamic blocks active'; - bouw=bouw+""; - $("#dynblock").html(bouw); + if (!gotsome) { + tableElement.append($('No dynamic blocks active')); + } + $("#dynblock").html(tableElement); }}); $.ajax({ url: 'jsonstat?command=ebpfblocklist', type: 'GET', dataType: 'json', jsonp: false, success: function(data) { - var bouw=''; - var gotsome=false; + var tableElement = $('
Kernel-based dyn blocked netmaskSecondsBlocks
'); + var gotsome = false; $.each(data, function(a,b) { - bouw=bouw+(""); - gotsome=true; + var row = $(''); + appendCellToRow(row, a); + appendCellToRow(row, b.seconds); + appendCellToRow(row, b.blocks); + tableElement.append(row); + gotsome = true; }); - if(!gotsome) - bouw = bouw + ''; - - bouw=bouw+"
Kernel-based dyn blocked netmaskSecondsBlocks
"+a+""+b.seconds+""+b.blocks+"
No eBPF blocks active
"; - $("#ebpfblock").html(bouw); + if (!gotsome) { + tableElement.append($('No eBPF blocks active')); + } + $("#ebpfblock").html(tableElement); }}); - }; - - $("#filter1").click(updateRingBuffers); - $("#filter2").click(updateRingBuffers); update(); setInterval(update, 1000); diff -Nru dnsdist-1.9.10/htmlfiles.h dnsdist-1.9.14/htmlfiles.h --- dnsdist-1.9.10/htmlfiles.h 2025-05-20 09:13:53.000000000 +0000 +++ dnsdist-1.9.14/htmlfiles.h 2026-04-22 16:47:33.000000000 +0000 @@ -24683,458 +24683,233 @@ }; static const unsigned char glocal_jsData[] = { 0x22, 0x75, 0x73, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x22, 0x3b, 0x0a, 0x0a, 0x76, - 0x61, 0x72, 0x20, 0x67, 0x64, 0x61, 0x74, 0x61, 0x3d, 0x7b, 0x7d, 0x0a, 0x0a, 0x24, 0x28, 0x64, - 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x29, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x79, 0x28, 0x66, - 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78, 0x53, 0x65, 0x74, 0x75, 0x70, 0x28, 0x7b, 0x20, 0x63, 0x61, - 0x63, 0x68, 0x65, 0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x71, 0x70, 0x73, 0x67, - 0x72, 0x61, 0x70, 0x68, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, - 0x68, 0x61, 0x77, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x28, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x64, 0x6f, - 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x71, 0x70, 0x73, 0x63, 0x68, 0x61, 0x72, 0x74, 0x22, - 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x3a, 0x20, 0x34, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x32, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x3a, 0x20, 0x27, 0x6c, - 0x69, 0x6e, 0x65, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, - 0x72, 0x69, 0x65, 0x73, 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, 0x68, - 0x61, 0x77, 0x2e, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x44, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x5b, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, - 0x20, 0x27, 0x73, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x70, 0x73, 0x27, 0x20, 0x7d, 0x2c, - 0x20, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x27, 0x71, 0x70, 0x73, 0x27, 0x7d, 0x5d, 0x2c, - 0x20, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2c, 0x20, 0x7b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x44, 0x61, 0x74, - 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x61, - 0x73, 0x65, 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x44, 0x61, 0x74, 0x65, 0x28, 0x29, 0x2e, 0x67, - 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x28, 0x29, 0x20, 0x2f, 0x20, 0x31, 0x30, 0x30, 0x30, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x20, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x79, 0x5f, 0x74, - 0x69, 0x63, 0x6b, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, - 0x68, 0x61, 0x77, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x41, 0x78, 0x69, 0x73, 0x2e, 0x59, - 0x28, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x72, 0x61, 0x70, - 0x68, 0x3a, 0x20, 0x71, 0x70, 0x73, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2c, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x3a, 0x20, 0x27, 0x6c, 0x65, 0x66, 0x74, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x69, 0x63, 0x6b, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x3a, 0x20, 0x52, 0x69, - 0x63, 0x6b, 0x73, 0x68, 0x61, 0x77, 0x2e, 0x46, 0x69, 0x78, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, - 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x4b, 0x4d, 0x42, - 0x54, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, - 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x71, 0x70, 0x73, - 0x79, 0x5f, 0x61, 0x78, 0x69, 0x73, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x29, - 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x71, 0x70, 0x73, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2e, - 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x28, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, - 0x61, 0x72, 0x20, 0x63, 0x70, 0x75, 0x67, 0x72, 0x61, 0x70, 0x68, 0x20, 0x3d, 0x20, 0x6e, 0x65, - 0x77, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, 0x68, 0x61, 0x77, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, - 0x28, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, - 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x63, 0x70, - 0x75, 0x63, 0x68, 0x61, 0x72, 0x74, 0x22, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x34, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x32, 0x30, - 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x65, 0x72, 0x3a, 0x20, 0x27, 0x6c, 0x69, 0x6e, 0x65, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x3a, 0x20, 0x6e, 0x65, 0x77, - 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, 0x68, 0x61, 0x77, 0x2e, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, - 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x5b, - 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x27, 0x6f, 0x6e, 0x65, 0x27, 0x20, 0x7d, 0x2c, - 0x20, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x27, 0x74, 0x77, 0x6f, 0x27, 0x7d, 0x5d, 0x2c, - 0x20, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2c, 0x20, 0x7b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x44, 0x61, 0x74, - 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x61, - 0x73, 0x65, 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x44, 0x61, 0x74, 0x65, 0x28, 0x29, 0x2e, 0x67, - 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x28, 0x29, 0x20, 0x2f, 0x20, 0x31, 0x30, 0x30, 0x30, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x7d, 0x20, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x79, 0x5f, 0x74, - 0x69, 0x63, 0x6b, 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, - 0x68, 0x61, 0x77, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x41, 0x78, 0x69, 0x73, 0x2e, 0x59, - 0x28, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x72, 0x61, 0x70, - 0x68, 0x3a, 0x20, 0x63, 0x70, 0x75, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2c, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x3a, 0x20, 0x27, 0x6c, 0x65, 0x66, 0x74, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x69, 0x63, 0x6b, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x3a, 0x20, 0x52, 0x69, - 0x63, 0x6b, 0x73, 0x68, 0x61, 0x77, 0x2e, 0x46, 0x69, 0x78, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, - 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x4b, 0x4d, 0x42, - 0x54, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, - 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x63, 0x70, 0x75, - 0x79, 0x5f, 0x61, 0x78, 0x69, 0x73, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x29, - 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x63, 0x70, 0x75, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2e, - 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, - 0x72, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3d, - 0x30, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x69, 0x6e, 0x67, 0x42, 0x75, 0x66, 0x66, 0x65, - 0x72, 0x73, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x3d, - 0x24, 0x28, 0x22, 0x23, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x31, 0x22, 0x29, 0x2e, 0x69, 0x73, - 0x28, 0x27, 0x3a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x71, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x3d, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x74, 0x3f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x3d, 0x67, 0x65, 0x74, 0x2d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2d, 0x72, 0x69, 0x6e, - 0x67, 0x26, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x27, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x65, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x71, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3d, 0x71, 0x73, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x2b, 0x22, 0x26, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x65, 0x64, 0x3d, 0x31, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x24, 0x2e, 0x67, 0x65, 0x74, 0x4a, 0x53, 0x4f, 0x4e, 0x28, 0x71, 0x73, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, - 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6e, - 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x22, - 0x3c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3e, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x4e, - 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x44, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x54, 0x79, 0x70, - 0x65, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x75, 0x6d, 0x3d, 0x30, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x3d, 0x30, - 0x2c, 0x20, 0x72, 0x65, 0x73, 0x74, 0x3d, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x65, 0x6e, 0x74, - 0x72, 0x69, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x28, 0x61, 0x2c, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x2b, 0x3d, 0x62, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x6e, 0x75, 0x6d, 0x2b, - 0x2b, 0x20, 0x3e, 0x20, 0x31, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x74, 0x2b, 0x3d, 0x62, 0x5b, - 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x69, 0x66, 0x28, 0x62, 0x5b, 0x31, 0x5d, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3e, - 0x20, 0x32, 0x35, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x62, 0x5b, 0x31, 0x5d, 0x3d, 0x62, 0x5b, 0x31, 0x5d, 0x2e, 0x73, 0x75, 0x62, - 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x30, 0x2c, 0x32, 0x35, 0x29, 0x3b, 0x0a, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x62, 0x6f, - 0x75, 0x77, 0x2b, 0x28, 0x22, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, - 0x5b, 0x30, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, - 0x62, 0x5b, 0x31, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, - 0x2b, 0x62, 0x5b, 0x32, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, - 0x3e, 0x22, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x3d, 0x22, 0x3c, 0x74, 0x72, 0x3e, - 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x72, 0x65, 0x73, 0x74, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, - 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x52, 0x65, 0x73, 0x74, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, - 0x74, 0x72, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, - 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3e, 0x22, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, 0x62, 0x6f, 0x75, - 0x77, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x3d, 0x24, 0x28, 0x22, - 0x23, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x32, 0x22, 0x29, 0x2e, 0x69, 0x73, 0x28, 0x27, 0x3a, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x64, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x71, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3d, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x73, - 0x74, 0x61, 0x74, 0x3f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x3d, 0x67, 0x65, 0x74, 0x2d, - 0x71, 0x75, 0x65, 0x72, 0x79, 0x2d, 0x72, 0x69, 0x6e, 0x67, 0x26, 0x6e, 0x61, 0x6d, 0x65, 0x3d, - 0x73, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x2d, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, - 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x66, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x71, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3d, 0x71, 0x73, 0x74, 0x72, - 0x69, 0x6e, 0x67, 0x2b, 0x22, 0x26, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x66, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x65, 0x64, 0x3d, 0x31, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x24, 0x2e, 0x67, 0x65, 0x74, 0x4a, 0x53, 0x4f, 0x4e, 0x28, 0x71, 0x73, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x76, 0x61, 0x72, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x22, 0x3c, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x3e, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3c, - 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x53, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, - 0x20, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, - 0x54, 0x79, 0x70, 0x65, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x75, 0x6d, 0x3d, 0x30, - 0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x3d, 0x30, 0x2c, 0x20, 0x72, 0x65, 0x73, 0x74, 0x3d, - 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, - 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x5d, 0x2c, - 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x62, 0x29, 0x20, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x2b, 0x3d, 0x62, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x69, 0x66, 0x28, 0x6e, 0x75, 0x6d, 0x2b, 0x2b, 0x20, 0x3e, 0x20, 0x31, 0x30, 0x29, - 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x72, 0x65, 0x73, 0x74, 0x2b, 0x3d, 0x62, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x62, 0x5b, 0x31, 0x5d, - 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x20, 0x3e, 0x20, 0x32, 0x35, 0x29, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x5b, 0x31, 0x5d, - 0x3d, 0x62, 0x5b, 0x31, 0x5d, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, - 0x30, 0x2c, 0x32, 0x35, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x28, 0x22, 0x3c, 0x74, 0x72, - 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x30, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, - 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x31, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, - 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x32, 0x5d, 0x2b, 0x22, 0x3c, - 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, - 0x77, 0x2b, 0x3d, 0x22, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x72, 0x65, - 0x73, 0x74, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x52, 0x65, 0x73, - 0x74, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x22, 0x3c, - 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x24, 0x28, 0x22, 0x23, 0x73, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, 0x62, 0x6f, 0x75, - 0x77, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x67, 0x65, 0x74, 0x4a, 0x53, 0x4f, 0x4e, 0x28, 0x27, 0x6a, - 0x73, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x74, 0x3f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x3d, - 0x67, 0x65, 0x74, 0x2d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x72, 0x69, 0x6e, 0x67, 0x26, - 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x27, 0x2c, 0x20, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, - 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x6f, 0x75, - 0x77, 0x3d, 0x22, 0x3c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3e, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, - 0x68, 0x3e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, - 0x3e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x72, - 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x75, - 0x6d, 0x3d, 0x30, 0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x3d, 0x30, 0x2c, 0x20, 0x72, 0x65, - 0x73, 0x74, 0x3d, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, - 0x63, 0x68, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, - 0x22, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x62, - 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x2b, 0x3d, 0x62, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x6e, 0x75, 0x6d, 0x2b, 0x2b, 0x20, 0x3e, 0x20, - 0x31, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x74, 0x20, 0x2b, 0x3d, 0x62, 0x5b, 0x30, 0x5d, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, - 0x77, 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x28, 0x22, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, - 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x30, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, - 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x31, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, - 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x3d, 0x22, 0x3c, - 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x72, 0x65, 0x73, 0x74, 0x2b, 0x22, 0x3c, - 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x52, 0x65, 0x73, 0x74, 0x3c, 0x2f, 0x74, 0x64, - 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, - 0x6f, 0x75, 0x77, 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x61, 0x72, 0x20, 0x67, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a, 0x0a, 0x24, + 0x28, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x29, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x79, + 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78, 0x53, 0x65, 0x74, 0x75, 0x70, 0x28, 0x7b, 0x20, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x20, 0x7d, 0x29, 0x3b, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x71, 0x70, 0x73, 0x67, 0x72, 0x61, + 0x70, 0x68, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, 0x68, 0x61, + 0x77, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x28, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, + 0x79, 0x49, 0x64, 0x28, 0x22, 0x71, 0x70, 0x73, 0x63, 0x68, 0x61, 0x72, 0x74, 0x22, 0x29, 0x2c, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, + 0x34, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x3a, 0x20, 0x32, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x3a, 0x20, 0x27, 0x6c, 0x69, 0x6e, + 0x65, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, 0x68, 0x61, 0x77, + 0x2e, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x5b, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x27, + 0x73, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x70, 0x73, 0x27, 0x20, 0x7d, 0x2c, 0x20, 0x7b, + 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x27, 0x71, 0x70, 0x73, 0x27, 0x7d, 0x5d, 0x2c, 0x20, 0x75, + 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2c, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x44, 0x61, 0x74, 0x61, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x61, 0x73, 0x65, + 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x44, 0x61, 0x74, 0x65, 0x28, 0x29, 0x2e, 0x67, 0x65, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x28, 0x29, 0x20, 0x2f, 0x20, 0x31, 0x30, 0x30, 0x30, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x79, 0x5f, 0x74, 0x69, 0x63, 0x6b, + 0x73, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, 0x68, 0x61, 0x77, + 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x41, 0x78, 0x69, 0x73, 0x2e, 0x59, 0x28, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x72, 0x61, 0x70, 0x68, 0x3a, 0x20, + 0x71, 0x70, 0x73, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x27, + 0x6c, 0x65, 0x66, 0x74, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x69, 0x63, 0x6b, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x3a, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, + 0x68, 0x61, 0x77, 0x2e, 0x46, 0x69, 0x78, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x2e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x4b, 0x4d, 0x42, 0x54, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, + 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x71, 0x70, 0x73, 0x79, 0x5f, 0x61, + 0x78, 0x69, 0x73, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x29, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x71, 0x70, 0x73, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x72, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x28, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, + 0x63, 0x70, 0x75, 0x67, 0x72, 0x61, 0x70, 0x68, 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, + 0x69, 0x63, 0x6b, 0x73, 0x68, 0x61, 0x77, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x28, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x3a, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x22, 0x63, 0x70, 0x75, 0x63, 0x68, + 0x61, 0x72, 0x74, 0x22, 0x29, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, + 0x69, 0x64, 0x74, 0x68, 0x3a, 0x20, 0x34, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x20, 0x32, 0x30, 0x30, 0x2c, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, + 0x3a, 0x20, 0x27, 0x6c, 0x69, 0x6e, 0x65, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x3a, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x69, + 0x63, 0x6b, 0x73, 0x68, 0x61, 0x77, 0x2e, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x46, 0x69, + 0x78, 0x65, 0x64, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x5b, 0x7b, 0x20, 0x6e, + 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x27, 0x6f, 0x6e, 0x65, 0x27, 0x20, 0x7d, 0x2c, 0x20, 0x7b, 0x6e, + 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x27, 0x74, 0x77, 0x6f, 0x27, 0x7d, 0x5d, 0x2c, 0x20, 0x75, 0x6e, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2c, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x76, 0x61, 0x6c, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x78, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x73, 0x3a, 0x20, 0x31, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x42, 0x61, 0x73, 0x65, 0x3a, + 0x20, 0x6e, 0x65, 0x77, 0x20, 0x44, 0x61, 0x74, 0x65, 0x28, 0x29, 0x2e, 0x67, 0x65, 0x74, 0x54, + 0x69, 0x6d, 0x65, 0x28, 0x29, 0x20, 0x2f, 0x20, 0x31, 0x30, 0x30, 0x30, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x79, 0x5f, 0x74, 0x69, 0x63, 0x6b, 0x73, + 0x20, 0x3d, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, 0x68, 0x61, 0x77, 0x2e, + 0x47, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x41, 0x78, 0x69, 0x73, 0x2e, 0x59, 0x28, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x72, 0x61, 0x70, 0x68, 0x3a, 0x20, 0x63, + 0x70, 0x75, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x27, 0x6c, + 0x65, 0x66, 0x74, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x69, + 0x63, 0x6b, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x3a, 0x20, 0x52, 0x69, 0x63, 0x6b, 0x73, 0x68, + 0x61, 0x77, 0x2e, 0x46, 0x69, 0x78, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x2e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x4b, 0x4d, 0x42, 0x54, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x20, + 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x27, 0x63, 0x70, 0x75, 0x79, 0x5f, 0x61, 0x78, + 0x69, 0x73, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x20, 0x29, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x63, 0x70, 0x75, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x72, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x28, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3d, 0x30, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, + 0x2c, 0x20, 0x75, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x61, 0x6c, 0x69, + 0x67, 0x6e, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x65, 0x6c, 0x6c, 0x20, 0x3d, 0x20, 0x24, 0x28, 0x27, + 0x3c, 0x74, 0x64, 0x2f, 0x3e, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x65, 0x6c, 0x6c, 0x20, 0x3d, + 0x20, 0x20, 0x24, 0x28, 0x27, 0x3c, 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x27, + 0x20, 0x2b, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x20, 0x2b, 0x20, 0x27, 0x2f, 0x3e, 0x27, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x63, 0x65, 0x6c, 0x6c, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x75, 0x6e, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x72, 0x6f, 0x77, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x28, 0x63, 0x65, 0x6c, + 0x6c, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x28, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, + 0x2e, 0x61, 0x6a, 0x61, 0x78, 0x28, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x74, 0x3f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x3d, 0x73, 0x74, 0x61, 0x74, 0x73, + 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x47, 0x45, 0x54, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, + 0x3a, 0x20, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x3a, 0x20, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x78, 0x2c, 0x20, 0x79, 0x29, 0x20, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x24, 0x28, 0x22, 0x23, 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x29, 0x2e, + 0x74, 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x71, 0x75, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x61, 0x63, 0x6c, 0x2d, 0x64, + 0x72, 0x6f, 0x70, 0x73, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, + 0x5b, 0x22, 0x61, 0x63, 0x6c, 0x2d, 0x64, 0x72, 0x6f, 0x70, 0x73, 0x22, 0x5d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x24, 0x28, 0x22, 0x23, 0x64, 0x79, 0x6e, 0x2d, 0x64, 0x72, 0x6f, 0x70, 0x73, 0x22, 0x29, 0x2e, + 0x74, 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x64, 0x79, 0x6e, 0x2d, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, - 0x28, 0x62, 0x6f, 0x75, 0x77, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x67, 0x65, 0x74, 0x4a, 0x53, 0x4f, - 0x4e, 0x28, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x74, 0x3f, 0x63, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x3d, 0x67, 0x65, 0x74, 0x2d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2d, 0x72, - 0x69, 0x6e, 0x67, 0x26, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x73, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, - 0x6c, 0x2d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x27, 0x2c, 0x20, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, - 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x22, - 0x3c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3e, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x4e, - 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x53, 0x65, - 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x3c, 0x2f, 0x74, - 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x76, 0x61, 0x72, 0x20, 0x6e, 0x75, 0x6d, 0x3d, 0x30, 0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x3d, 0x30, 0x2c, 0x20, 0x72, 0x65, 0x73, 0x74, 0x3d, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x65, - 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x2b, 0x3d, 0x62, 0x5b, 0x30, 0x5d, 0x3b, + 0x75, 0x6c, 0x65, 0x2d, 0x64, 0x72, 0x6f, 0x70, 0x73, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, + 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x72, 0x75, 0x6c, 0x65, 0x2d, 0x64, 0x72, 0x6f, 0x70, + 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x22, + 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x6d, 0x6f, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x64, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x75, 0x70, 0x74, + 0x69, 0x6d, 0x65, 0x22, 0x5d, 0x2a, 0x31, 0x30, 0x30, 0x30, 0x2e, 0x30, 0x29, 0x2e, 0x68, 0x75, + 0x6d, 0x61, 0x6e, 0x69, 0x7a, 0x65, 0x28, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x6c, + 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x28, 0x64, + 0x61, 0x74, 0x61, 0x5b, 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x61, 0x76, 0x67, + 0x31, 0x30, 0x30, 0x30, 0x30, 0x22, 0x5d, 0x2f, 0x31, 0x30, 0x30, 0x30, 0x2e, 0x30, 0x29, 0x2e, + 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, + 0x23, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x74, 0x63, 0x70, 0x22, 0x29, 0x2e, 0x74, + 0x65, 0x78, 0x74, 0x28, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, + 0x63, 0x79, 0x2d, 0x74, 0x63, 0x70, 0x2d, 0x61, 0x76, 0x67, 0x31, 0x30, 0x30, 0x30, 0x30, 0x22, + 0x5d, 0x2f, 0x31, 0x30, 0x30, 0x30, 0x2e, 0x30, 0x29, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, + 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x6c, 0x61, 0x74, 0x65, 0x6e, + 0x63, 0x79, 0x2d, 0x64, 0x6f, 0x74, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x28, 0x64, + 0x61, 0x74, 0x61, 0x5b, 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x64, 0x6f, 0x74, + 0x2d, 0x61, 0x76, 0x67, 0x31, 0x30, 0x30, 0x30, 0x30, 0x22, 0x5d, 0x2f, 0x31, 0x30, 0x30, 0x30, + 0x2e, 0x30, 0x29, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x6e, 0x75, - 0x6d, 0x2b, 0x2b, 0x20, 0x3e, 0x20, 0x31, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x74, 0x20, 0x2b, - 0x3d, 0x20, 0x62, 0x5b, 0x30, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x28, 0x22, - 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x30, 0x5d, 0x2b, 0x22, - 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x31, 0x5d, 0x2b, - 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, - 0x6f, 0x75, 0x77, 0x2b, 0x3d, 0x22, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, - 0x72, 0x65, 0x73, 0x74, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x52, - 0x65, 0x73, 0x74, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, - 0x22, 0x3c, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x73, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, - 0x62, 0x6f, 0x75, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78, 0x28, 0x7b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x72, 0x6c, - 0x3a, 0x20, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x74, 0x3f, 0x63, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x3d, 0x73, 0x74, 0x61, 0x74, 0x73, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x47, - 0x45, 0x54, 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x64, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x6a, 0x73, 0x6f, 0x6e, - 0x27, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6a, - 0x73, 0x6f, 0x6e, 0x70, 0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, - 0x20, 0x78, 0x2c, 0x20, 0x79, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, - 0x74, 0x61, 0x5b, 0x22, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x5d, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x24, 0x28, 0x22, 0x23, 0x61, 0x63, 0x6c, 0x2d, 0x64, 0x72, 0x6f, 0x70, 0x73, 0x22, 0x29, 0x2e, - 0x74, 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x61, 0x63, 0x6c, 0x2d, 0x64, - 0x72, 0x6f, 0x70, 0x73, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x64, 0x79, 0x6e, - 0x2d, 0x64, 0x72, 0x6f, 0x70, 0x73, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, - 0x74, 0x61, 0x5b, 0x22, 0x64, 0x79, 0x6e, 0x2d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x22, - 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x72, 0x75, 0x6c, 0x65, 0x2d, 0x64, 0x72, 0x6f, - 0x70, 0x73, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, - 0x72, 0x75, 0x6c, 0x65, 0x2d, 0x64, 0x72, 0x6f, 0x70, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, - 0x22, 0x23, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, - 0x6d, 0x6f, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, - 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x5d, 0x2a, 0x31, - 0x30, 0x30, 0x30, 0x2e, 0x30, 0x29, 0x2e, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x69, 0x7a, 0x65, 0x28, - 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, - 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x6c, 0x61, - 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x61, 0x76, 0x67, 0x31, 0x30, 0x30, 0x30, 0x30, 0x22, 0x5d, - 0x2f, 0x31, 0x30, 0x30, 0x30, 0x2e, 0x30, 0x29, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, - 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, - 0x79, 0x2d, 0x74, 0x63, 0x70, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x28, 0x64, 0x61, - 0x74, 0x61, 0x5b, 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x74, 0x63, 0x70, 0x2d, - 0x61, 0x76, 0x67, 0x31, 0x30, 0x30, 0x30, 0x30, 0x22, 0x5d, 0x2f, 0x31, 0x30, 0x30, 0x30, 0x2e, - 0x30, 0x29, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x24, 0x28, 0x22, 0x23, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x64, 0x6f, 0x74, 0x22, - 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x6c, 0x61, - 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x64, 0x6f, 0x74, 0x2d, 0x61, 0x76, 0x67, 0x31, 0x30, 0x30, - 0x30, 0x30, 0x22, 0x5d, 0x2f, 0x31, 0x30, 0x30, 0x30, 0x2e, 0x30, 0x29, 0x2e, 0x74, 0x6f, 0x46, - 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x6c, 0x61, - 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x64, 0x6f, 0x68, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, - 0x28, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, - 0x64, 0x6f, 0x68, 0x2d, 0x61, 0x76, 0x67, 0x31, 0x30, 0x30, 0x30, 0x30, 0x22, 0x5d, 0x2f, 0x31, - 0x30, 0x30, 0x30, 0x2e, 0x30, 0x29, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, - 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, - 0x64, 0x6f, 0x71, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x28, 0x64, 0x61, 0x74, 0x61, - 0x5b, 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x64, 0x6f, 0x71, 0x2d, 0x61, 0x76, - 0x67, 0x31, 0x30, 0x30, 0x30, 0x30, 0x22, 0x5d, 0x2f, 0x31, 0x30, 0x30, 0x30, 0x2e, 0x30, 0x29, - 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, - 0x28, 0x21, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x70, 0x75, 0x2d, 0x73, 0x79, 0x73, - 0x2d, 0x6d, 0x73, 0x65, 0x63, 0x22, 0x5d, 0x29, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x64, - 0x61, 0x74, 0x61, 0x3d, 0x64, 0x61, 0x74, 0x61, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, - 0x70, 0x75, 0x3d, 0x28, 0x28, 0x31, 0x2e, 0x30, 0x2a, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, - 0x70, 0x75, 0x2d, 0x73, 0x79, 0x73, 0x2d, 0x6d, 0x73, 0x65, 0x63, 0x22, 0x5d, 0x2b, 0x31, 0x2e, - 0x30, 0x2a, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x70, 0x75, 0x2d, 0x75, 0x73, 0x65, 0x72, - 0x2d, 0x6d, 0x73, 0x65, 0x63, 0x22, 0x5d, 0x20, 0x2d, 0x20, 0x31, 0x2e, 0x30, 0x2a, 0x67, 0x64, - 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x70, 0x75, 0x2d, 0x73, 0x79, 0x73, 0x2d, 0x6d, 0x73, 0x65, - 0x63, 0x22, 0x5d, 0x2d, 0x31, 0x2e, 0x30, 0x2a, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, - 0x70, 0x75, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x6d, 0x73, 0x65, 0x63, 0x22, 0x5d, 0x29, 0x2f, - 0x31, 0x30, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x63, 0x70, 0x75, 0x22, - 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x63, 0x70, 0x75, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, - 0x65, 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x71, 0x70, 0x73, 0x3d, - 0x31, 0x2e, 0x30, 0x2a, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, - 0x73, 0x22, 0x5d, 0x2d, 0x31, 0x2e, 0x30, 0x2a, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x71, - 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x71, 0x70, - 0x73, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x71, 0x70, 0x73, 0x2e, 0x74, 0x6f, 0x46, - 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2d, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x29, 0x2e, 0x74, 0x65, - 0x78, 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2d, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, - 0x73, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x70, 0x73, 0x3d, 0x31, 0x2e, 0x30, 0x2a, 0x64, - 0x61, 0x74, 0x61, 0x5b, 0x22, 0x73, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x2d, 0x72, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x5d, 0x2d, 0x31, 0x2e, 0x30, 0x2a, 0x67, 0x64, + 0x20, 0x24, 0x28, 0x22, 0x23, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x64, 0x6f, 0x68, + 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x6c, + 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x64, 0x6f, 0x68, 0x2d, 0x61, 0x76, 0x67, 0x31, 0x30, + 0x30, 0x30, 0x30, 0x22, 0x5d, 0x2f, 0x31, 0x30, 0x30, 0x30, 0x2e, 0x30, 0x29, 0x2e, 0x74, 0x6f, + 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x6c, + 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2d, 0x64, 0x6f, 0x71, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, + 0x74, 0x28, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, + 0x2d, 0x64, 0x6f, 0x71, 0x2d, 0x61, 0x76, 0x67, 0x31, 0x30, 0x30, 0x30, 0x30, 0x22, 0x5d, 0x2f, + 0x31, 0x30, 0x30, 0x30, 0x2e, 0x30, 0x29, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, + 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, + 0x22, 0x63, 0x70, 0x75, 0x2d, 0x73, 0x79, 0x73, 0x2d, 0x6d, 0x73, 0x65, 0x63, 0x22, 0x5d, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x64, + 0x61, 0x74, 0x61, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x63, 0x70, 0x75, 0x20, + 0x3d, 0x20, 0x28, 0x28, 0x31, 0x2a, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x70, 0x75, 0x2d, + 0x73, 0x79, 0x73, 0x2d, 0x6d, 0x73, 0x65, 0x63, 0x22, 0x5d, 0x20, 0x2b, 0x20, 0x31, 0x2a, 0x64, + 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x70, 0x75, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x6d, 0x73, + 0x65, 0x63, 0x22, 0x5d, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x31, 0x2a, 0x67, 0x64, 0x61, 0x74, 0x61, + 0x5b, 0x22, 0x63, 0x70, 0x75, 0x2d, 0x73, 0x79, 0x73, 0x2d, 0x6d, 0x73, 0x65, 0x63, 0x22, 0x5d, + 0x20, 0x2b, 0x20, 0x31, 0x2a, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x70, 0x75, 0x2d, + 0x75, 0x73, 0x65, 0x72, 0x2d, 0x6d, 0x73, 0x65, 0x63, 0x22, 0x5d, 0x29, 0x29, 0x2f, 0x31, 0x30, + 0x2e, 0x30, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x63, 0x70, 0x75, 0x22, 0x29, 0x2e, 0x74, + 0x65, 0x78, 0x74, 0x28, 0x63, 0x70, 0x75, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, + 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x71, 0x70, 0x73, 0x20, 0x3d, 0x20, 0x31, + 0x2e, 0x30, 0x2a, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x22, 0x5d, 0x2d, 0x31, 0x2e, 0x30, 0x2a, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x71, 0x75, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x71, 0x70, 0x73, + 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x71, 0x70, 0x73, 0x2e, 0x74, 0x6f, 0x46, 0x69, + 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2d, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, + 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2d, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x70, 0x73, 0x20, 0x3d, 0x20, 0x28, 0x31, 0x2a, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x73, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x2d, 0x72, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x5d, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, - 0x74, 0x6f, 0x74, 0x70, 0x63, 0x61, 0x63, 0x68, 0x65, 0x3d, 0x31, 0x2e, 0x30, 0x2a, 0x64, 0x61, - 0x74, 0x61, 0x5b, 0x22, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x68, 0x69, 0x74, 0x73, 0x22, 0x5d, - 0x2d, 0x31, 0x2e, 0x30, 0x2a, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x2d, 0x68, 0x69, 0x74, 0x73, 0x22, 0x5d, 0x2b, 0x31, 0x2e, 0x30, 0x2a, 0x64, 0x61, 0x74, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x5d, 0x29, 0x20, 0x2d, 0x20, 0x28, 0x31, 0x2a, + 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x73, 0x65, 0x72, 0x76, 0x66, 0x61, 0x69, 0x6c, 0x2d, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, + 0x61, 0x72, 0x20, 0x74, 0x6f, 0x74, 0x70, 0x63, 0x61, 0x63, 0x68, 0x65, 0x20, 0x3d, 0x20, 0x28, + 0x31, 0x2a, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x68, 0x69, + 0x74, 0x73, 0x22, 0x5d, 0x20, 0x2b, 0x20, 0x31, 0x2a, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x2d, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x73, 0x22, 0x5d, 0x29, 0x20, 0x2d, + 0x20, 0x28, 0x31, 0x2a, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x2d, 0x68, 0x69, 0x74, 0x73, 0x22, 0x5d, 0x20, 0x2b, 0x20, 0x31, 0x2a, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x73, 0x22, + 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x68, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x20, + 0x3d, 0x20, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x74, 0x6f, 0x74, 0x70, 0x63, 0x61, 0x63, + 0x68, 0x65, 0x20, 0x3e, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x31, 0x30, 0x30, 0x2e, 0x30, 0x2a, 0x28, 0x64, + 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x68, 0x69, 0x74, 0x73, 0x22, 0x5d, 0x2d, 0x31, 0x2e, 0x30, 0x2a, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x61, 0x63, - 0x68, 0x65, 0x2d, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x73, 0x22, 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, - 0x20, 0x68, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x3d, 0x30, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x74, - 0x6f, 0x74, 0x70, 0x63, 0x61, 0x63, 0x68, 0x65, 0x20, 0x3e, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x68, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x30, 0x30, 0x2e, - 0x30, 0x2a, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x68, - 0x69, 0x74, 0x73, 0x22, 0x5d, 0x2d, 0x31, 0x2e, 0x30, 0x2a, 0x67, 0x64, 0x61, 0x74, 0x61, 0x5b, - 0x22, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x68, 0x69, 0x74, 0x73, 0x22, 0x5d, 0x29, 0x2f, 0x74, - 0x6f, 0x74, 0x70, 0x63, 0x61, 0x63, 0x68, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, - 0x22, 0x23, 0x70, 0x68, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, - 0x74, 0x28, 0x68, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, - 0x64, 0x28, 0x32, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, + 0x68, 0x65, 0x2d, 0x68, 0x69, 0x74, 0x73, 0x22, 0x5d, 0x29, 0x2f, 0x74, 0x6f, 0x74, 0x70, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x70, 0x68, + 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x68, 0x69, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x70, 0x68, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x22, 0x29, - 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x24, 0x28, 0x22, 0x23, 0x70, 0x68, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x22, 0x29, 0x2e, 0x74, + 0x65, 0x78, 0x74, 0x28, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x71, 0x70, 0x73, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x61, 0x64, 0x64, 0x44, 0x61, 0x74, 0x61, 0x28, 0x7b, 0x20, 0x71, 0x70, 0x73, 0x3a, 0x20, 0x71, 0x70, 0x73, 0x2c, 0x20, 0x73, @@ -25149,237 +24924,281 @@ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x70, 0x75, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x28, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x64, 0x61, 0x74, 0x61, 0x3d, 0x64, 0x61, 0x74, 0x61, 0x3b, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x3a, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x20, 0x7b, - 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, - 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, - 0x61, 0x6a, 0x61, 0x78, 0x28, 0x7b, 0x20, 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x27, 0x61, 0x70, 0x69, - 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x6c, 0x6f, 0x63, 0x61, - 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x27, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x47, - 0x45, 0x54, 0x27, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, - 0x6a, 0x73, 0x6f, 0x6e, 0x27, 0x2c, 0x20, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x3a, 0x20, 0x66, 0x61, - 0x6c, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x20, 0x66, - 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, 0x0a, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x5d, 0x2b, 0x22, 0x20, 0x22, 0x2b, - 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x61, 0x63, 0x6c, 0x22, 0x29, - 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x61, 0x63, 0x6c, 0x22, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, + 0x61, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, + 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x3a, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, + 0x20, 0x7b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78, 0x28, 0x7b, + 0x20, 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x27, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x73, 0x2f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x27, + 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x47, 0x45, 0x54, 0x27, 0x2c, 0x20, 0x64, + 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x27, 0x2c, + 0x20, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, + 0x28, 0x22, 0x23, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, + 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x22, 0x5d, 0x2b, 0x22, 0x20, 0x22, 0x2b, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x24, 0x28, 0x22, 0x23, 0x61, 0x63, 0x6c, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, + 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x61, 0x63, 0x6c, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x22, 0x29, 0x2e, 0x74, + 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x6c, 0x6f, 0x63, - 0x61, 0x6c, 0x22, 0x29, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, - 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, - 0x61, 0x72, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x27, 0x3c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, - 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x30, 0x30, 0x25, 0x22, 0x3e, 0x3c, 0x74, 0x72, - 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3e, 0x3c, 0x74, 0x68, - 0x3e, 0x23, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, - 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, - 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x55, 0x44, 0x50, - 0x20, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, - 0x3e, 0x54, 0x43, 0x50, 0x20, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x3c, 0x2f, 0x74, 0x68, - 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x51, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x3c, 0x2f, 0x74, 0x68, - 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x44, 0x72, 0x6f, 0x70, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, - 0x74, 0x68, 0x3e, 0x51, 0x50, 0x53, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x4f, - 0x75, 0x74, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x57, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x3c, - 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, - 0x66, 0x74, 0x3e, 0x50, 0x6f, 0x6f, 0x6c, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, - 0x72, 0x3e, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, - 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x22, 0x5d, - 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x62, 0x29, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, - 0x3d, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, 0x28, 0x22, 0x3c, 0x74, 0x72, 0x20, 0x61, - 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, - 0x2b, 0x62, 0x5b, 0x22, 0x69, 0x64, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, - 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x22, 0x2b, - 0x62, 0x5b, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, - 0x3c, 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x22, - 0x2b, 0x62, 0x5b, 0x22, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x5d, 0x2b, 0x22, 0x3c, - 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x22, 0x29, 0x3b, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x61, 0x74, 0x65, - 0x6e, 0x63, 0x79, 0x20, 0x3d, 0x20, 0x28, 0x62, 0x5b, 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, - 0x79, 0x22, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x7c, 0x7c, 0x20, - 0x62, 0x5b, 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, - 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x22, 0x2d, 0x22, 0x20, 0x3a, 0x20, 0x62, 0x5b, - 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5d, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, - 0x65, 0x64, 0x28, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x76, 0x61, 0x72, 0x20, 0x74, 0x63, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x20, 0x3d, - 0x20, 0x28, 0x62, 0x5b, 0x22, 0x74, 0x63, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, - 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x7c, 0x7c, 0x20, 0x62, 0x5b, - 0x22, 0x74, 0x63, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5d, 0x20, 0x3d, 0x3d, - 0x3d, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x22, 0x2d, 0x22, 0x20, 0x3a, 0x20, 0x62, - 0x5b, 0x22, 0x74, 0x63, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5d, 0x2e, 0x74, - 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x3d, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, - 0x2b, 0x20, 0x28, 0x22, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, - 0x79, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x74, 0x63, - 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, - 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, - 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, - 0x22, 0x72, 0x65, 0x75, 0x73, 0x65, 0x64, 0x73, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, - 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x28, 0x62, 0x5b, 0x22, 0x71, 0x70, 0x73, 0x22, 0x5d, - 0x29, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x2b, 0x22, 0x3c, 0x2f, - 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, 0x6f, 0x75, 0x74, 0x73, - 0x74, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, - 0x22, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, - 0x77, 0x20, 0x3d, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, 0x28, 0x22, 0x3c, 0x74, 0x64, - 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x5d, 0x2b, 0x22, - 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x20, - 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, - 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, - 0x74, 0x72, 0x3e, 0x22, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x24, 0x28, 0x27, 0x3c, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x30, 0x30, + 0x25, 0x22, 0x3e, 0x3c, 0x74, 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x72, 0x69, 0x67, + 0x68, 0x74, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x23, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, + 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x4e, 0x61, 0x6d, 0x65, + 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, + 0x65, 0x66, 0x74, 0x3e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, + 0x3c, 0x74, 0x68, 0x3e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, + 0x74, 0x68, 0x3e, 0x55, 0x44, 0x50, 0x20, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x3c, 0x2f, + 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x54, 0x43, 0x50, 0x20, 0x4c, 0x61, 0x74, 0x65, 0x6e, + 0x63, 0x79, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x51, 0x75, 0x65, 0x72, 0x69, + 0x65, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x44, 0x72, 0x6f, 0x70, 0x73, + 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x51, 0x50, 0x53, 0x3c, 0x2f, 0x74, 0x68, + 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x4f, 0x75, 0x74, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, + 0x3e, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, + 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x50, 0x6f, 0x6f, 0x6c, 0x73, 0x3c, 0x2f, + 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x3c, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3e, + 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, + 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x22, 0x5d, 0x2c, + 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x3d, 0x20, 0x62, 0x6f, 0x75, - 0x77, 0x20, 0x2b, 0x20, 0x22, 0x3c, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3e, 0x22, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x6f, + 0x77, 0x20, 0x3d, 0x20, 0x24, 0x28, 0x27, 0x3c, 0x74, 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, + 0x3d, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2f, 0x3e, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, + 0x20, 0x3d, 0x20, 0x28, 0x62, 0x5b, 0x22, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5d, + 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x7c, 0x7c, 0x20, 0x62, 0x5b, 0x22, + 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, 0x2e, + 0x30, 0x29, 0x20, 0x3f, 0x20, 0x22, 0x2d, 0x22, 0x20, 0x3a, 0x20, 0x62, 0x5b, 0x22, 0x6c, 0x61, + 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5d, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, + 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, + 0x20, 0x74, 0x63, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x20, 0x3d, 0x20, 0x28, 0x62, + 0x5b, 0x22, 0x74, 0x63, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5d, 0x20, 0x3d, + 0x3d, 0x3d, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x20, 0x7c, 0x7c, 0x20, 0x62, 0x5b, 0x22, 0x74, 0x63, + 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5d, 0x20, 0x3d, 0x3d, 0x3d, 0x20, 0x30, + 0x2e, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x22, 0x2d, 0x22, 0x20, 0x3a, 0x20, 0x62, 0x5b, 0x22, 0x74, + 0x63, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x5d, 0x2e, 0x74, 0x6f, 0x46, 0x69, + 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, + 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x69, 0x64, 0x22, 0x5d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, + 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, + 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x2c, 0x20, 0x27, 0x6c, 0x65, 0x66, 0x74, 0x27, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, + 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, + 0x62, 0x5b, 0x22, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x5d, 0x2c, 0x20, 0x27, 0x6c, + 0x65, 0x66, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x73, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, 0x62, 0x6f, 0x75, 0x77, + 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, + 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x5d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, + 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, + 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, + 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x74, 0x63, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, + 0x63, 0x79, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, + 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, + 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x22, 0x5d, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, + 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, + 0x62, 0x5b, 0x22, 0x72, 0x65, 0x75, 0x73, 0x65, 0x64, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, + 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x71, + 0x70, 0x73, 0x22, 0x5d, 0x2e, 0x74, 0x6f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x28, 0x32, 0x29, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, + 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, + 0x62, 0x5b, 0x22, 0x6f, 0x75, 0x74, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, - 0x75, 0x77, 0x3d, 0x27, 0x3c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, - 0x3d, 0x22, 0x31, 0x30, 0x30, 0x25, 0x22, 0x3e, 0x3c, 0x74, 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, - 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x23, 0x3c, 0x2f, 0x74, 0x68, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, + 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, + 0x20, 0x62, 0x5b, 0x22, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, + 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, + 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x70, 0x6f, 0x6f, 0x6c, 0x73, + 0x22, 0x5d, 0x2c, 0x20, 0x27, 0x6c, 0x65, 0x66, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x28, 0x72, 0x6f, 0x77, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, + 0x22, 0x23, 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x73, 0x22, 0x29, 0x2e, + 0x68, 0x74, 0x6d, 0x6c, 0x28, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x24, 0x28, 0x27, 0x3c, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x30, 0x30, 0x25, 0x22, 0x3e, + 0x3c, 0x74, 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x3c, + 0x74, 0x68, 0x3e, 0x23, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, + 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, - 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, - 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x52, 0x75, 0x6c, 0x65, 0x3c, 0x2f, 0x74, 0x68, + 0x52, 0x75, 0x6c, 0x65, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x4d, 0x61, 0x74, 0x63, + 0x68, 0x65, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x3c, 0x2f, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x3e, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x5d, 0x2e, + 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, + 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x28, 0x61, 0x2c, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x6f, 0x77, 0x20, 0x3d, 0x20, + 0x24, 0x28, 0x27, 0x3c, 0x74, 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, + 0x74, 0x20, 0x2f, 0x3e, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, + 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x69, 0x64, 0x22, + 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, + 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x2c, + 0x20, 0x27, 0x6c, 0x65, 0x66, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, + 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x72, + 0x75, 0x6c, 0x65, 0x22, 0x5d, 0x2c, 0x20, 0x27, 0x6c, 0x65, 0x66, 0x74, 0x27, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, + 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, + 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0x5d, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, + 0x64, 0x28, 0x72, 0x6f, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x70, + 0x70, 0x65, 0x6e, 0x64, 0x28, 0x24, 0x28, 0x27, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x20, + 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x22, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x20, 0x63, + 0x6f, 0x6c, 0x73, 0x70, 0x61, 0x6e, 0x3d, 0x22, 0x34, 0x22, 0x3e, 0x3c, 0x66, 0x6f, 0x6e, 0x74, + 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3d, 0x22, 0x23, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x22, + 0x3e, 0x4e, 0x6f, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, + 0x64, 0x3c, 0x2f, 0x66, 0x6f, 0x6e, 0x74, 0x3e, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, + 0x72, 0x3e, 0x27, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x29, 0x2e, 0x68, + 0x74, 0x6d, 0x6c, 0x28, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x24, 0x28, 0x27, 0x3c, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x30, 0x30, 0x25, 0x22, 0x3e, 0x3c, + 0x74, 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x3c, 0x74, + 0x68, 0x3e, 0x23, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, 0x67, + 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x4e, 0x61, 0x6d, 0x65, 0x3c, 0x2f, 0x74, 0x68, 0x3e, + 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x52, 0x75, 0x6c, 0x65, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, - 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, - 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x5d, 0x2e, 0x6c, 0x65, - 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x72, 0x75, - 0x6c, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, - 0x61, 0x2c, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x3d, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, - 0x2b, 0x20, 0x28, 0x22, 0x3c, 0x74, 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, - 0x66, 0x74, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, 0x69, 0x64, 0x22, 0x5d, - 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, - 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, - 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, 0x72, 0x75, 0x6c, 0x65, - 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, - 0x5b, 0x22, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, - 0x3e, 0x22, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x3d, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, - 0x28, 0x22, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x65, 0x73, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, - 0x22, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, - 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, - 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, - 0x20, 0x3d, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, 0x27, 0x3c, 0x74, 0x72, 0x3e, 0x3c, - 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x22, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, - 0x22, 0x20, 0x63, 0x6f, 0x6c, 0x73, 0x70, 0x61, 0x6e, 0x3d, 0x22, 0x34, 0x22, 0x3e, 0x3c, 0x66, - 0x6f, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3d, 0x22, 0x23, 0x61, 0x61, 0x61, 0x61, - 0x61, 0x61, 0x22, 0x3e, 0x4e, 0x6f, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x64, 0x3c, 0x2f, 0x66, 0x6f, 0x6e, 0x74, 0x3e, 0x3c, 0x2f, 0x74, 0x64, 0x3e, - 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, - 0x77, 0x20, 0x3d, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, 0x22, 0x3c, 0x2f, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, - 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, 0x62, 0x6f, 0x75, - 0x77, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x27, - 0x3c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x30, - 0x30, 0x25, 0x22, 0x3e, 0x3c, 0x74, 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, - 0x66, 0x74, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x23, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, - 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x4e, 0x61, 0x6d, 0x65, - 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, - 0x65, 0x66, 0x74, 0x3e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x52, 0x75, 0x6c, - 0x65, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, - 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x69, 0x66, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2d, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x5d, 0x2e, 0x6c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, - 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2d, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x5d, 0x2c, 0x20, 0x66, 0x75, - 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, - 0x3d, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, 0x28, 0x22, 0x3c, 0x74, 0x72, 0x20, 0x61, - 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, - 0x62, 0x5b, 0x22, 0x69, 0x64, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, - 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x22, 0x2b, 0x62, - 0x5b, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, - 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x22, 0x2b, - 0x62, 0x5b, 0x22, 0x72, 0x75, 0x6c, 0x65, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, - 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x5b, 0x22, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x22, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x3d, 0x20, - 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, 0x28, 0x22, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, - 0x5b, 0x22, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0x5d, 0x2b, 0x22, 0x3c, 0x2f, 0x74, - 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x3c, 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3e, 0x27, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x22, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2d, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x5d, + 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5b, + 0x22, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2d, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, + 0x5d, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x62, 0x29, + 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, + 0x76, 0x61, 0x72, 0x20, 0x72, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x24, 0x28, 0x27, 0x3c, 0x74, 0x72, + 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x2f, 0x3e, 0x27, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, + 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x69, 0x64, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, + 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, + 0x62, 0x5b, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x2c, 0x20, 0x27, 0x6c, 0x65, 0x66, 0x74, + 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, + 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x72, 0x75, 0x6c, 0x65, 0x22, 0x5d, 0x2c, + 0x20, 0x27, 0x6c, 0x65, 0x66, 0x74, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, + 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, + 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x5b, 0x22, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0x5d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x28, 0x72, 0x6f, 0x77, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x3d, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, - 0x27, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x22, - 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x20, 0x63, 0x6f, 0x6c, 0x73, 0x70, 0x61, 0x6e, 0x3d, - 0x22, 0x34, 0x22, 0x3e, 0x3c, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3d, - 0x22, 0x23, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x22, 0x3e, 0x4e, 0x6f, 0x20, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x64, 0x65, 0x66, 0x69, - 0x6e, 0x65, 0x64, 0x3c, 0x2f, 0x66, 0x6f, 0x6e, 0x74, 0x3e, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, - 0x2f, 0x74, 0x72, 0x3e, 0x27, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, - 0x20, 0x3d, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, 0x22, 0x3c, 0x2f, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2d, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x29, 0x2e, - 0x68, 0x74, 0x6d, 0x6c, 0x28, 0x62, 0x6f, 0x75, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, - 0x3b, 0x0a, 0x0a, 0x0a, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, - 0x28, 0x28, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2b, - 0x2b, 0x29, 0x25, 0x35, 0x29, 0x0a, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x2f, 0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x69, 0x6e, 0x67, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x73, 0x28, 0x29, 0x3b, - 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78, - 0x28, 0x7b, 0x20, 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x73, 0x74, 0x61, - 0x74, 0x3f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x3d, 0x64, 0x79, 0x6e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x27, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, - 0x47, 0x45, 0x54, 0x27, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, - 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x27, 0x2c, 0x20, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x3a, 0x20, 0x66, - 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x20, - 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x28, 0x24, + 0x28, 0x27, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, + 0x22, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x20, 0x63, 0x6f, 0x6c, 0x73, 0x70, 0x61, 0x6e, + 0x3d, 0x22, 0x34, 0x22, 0x3e, 0x3c, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, + 0x3d, 0x22, 0x23, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x22, 0x3e, 0x4e, 0x6f, 0x20, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x64, 0x3c, 0x2f, 0x66, 0x6f, 0x6e, 0x74, 0x3e, 0x3c, 0x2f, 0x74, 0x64, 0x3e, + 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x27, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x27, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2d, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, + 0x61, 0x6a, 0x61, 0x78, 0x28, 0x7b, 0x20, 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x27, 0x6a, 0x73, 0x6f, + 0x6e, 0x73, 0x74, 0x61, 0x74, 0x3f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x3d, 0x64, 0x79, + 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x27, 0x2c, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x3a, 0x20, 0x27, 0x47, 0x45, 0x54, 0x27, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x54, 0x79, + 0x70, 0x65, 0x3a, 0x20, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x27, 0x2c, 0x20, 0x6a, 0x73, 0x6f, 0x6e, + 0x70, 0x3a, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x3a, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, + 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x24, 0x28, 0x27, 0x3c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x30, 0x30, 0x25, 0x22, 0x3e, 0x3c, 0x74, 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x44, 0x79, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, @@ -25388,102 +25207,127 @@ 0x74, 0x68, 0x3e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x65, 0x42, 0x50, 0x46, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x27, 0x3b, 0x0a, 0x09, 0x09, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x67, 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x3d, - 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, - 0x63, 0x68, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x28, 0x61, 0x2c, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x3c, 0x2f, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x3e, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x67, + 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x28, 0x22, 0x3c, 0x74, - 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x61, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, - 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x2e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2b, - 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x73, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, - 0x2b, 0x62, 0x2e, 0x65, 0x62, 0x70, 0x66, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, - 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x2e, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x2b, 0x22, 0x3c, 0x2f, - 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x09, 0x20, - 0x67, 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x3d, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x09, 0x09, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x21, 0x67, 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x29, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x64, 0x61, 0x74, 0x61, + 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x62, 0x29, 0x20, + 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, + 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x24, 0x28, 0x27, 0x3c, 0x74, 0x72, 0x2f, 0x3e, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x3d, - 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, 0x27, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, - 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x22, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x20, - 0x63, 0x6f, 0x6c, 0x73, 0x70, 0x61, 0x6e, 0x3d, 0x22, 0x34, 0x22, 0x3e, 0x3c, 0x66, 0x6f, 0x6e, - 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3d, 0x22, 0x23, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, - 0x22, 0x3e, 0x4e, 0x6f, 0x20, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x20, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x73, 0x20, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x3c, 0x2f, 0x66, 0x6f, 0x6e, 0x74, - 0x3e, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x27, 0x3b, 0x0a, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, + 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x61, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, + 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, + 0x20, 0x62, 0x2e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, + 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x2e, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, + 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, + 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x2e, 0x65, 0x62, 0x70, 0x66, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, + 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x2e, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, + 0x6e, 0x64, 0x28, 0x72, 0x6f, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x22, 0x3c, - 0x2f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, - 0x28, 0x22, 0x23, 0x64, 0x79, 0x6e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x29, 0x2e, 0x68, 0x74, - 0x6d, 0x6c, 0x28, 0x62, 0x6f, 0x75, 0x77, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x7d, 0x29, 0x3b, - 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78, - 0x28, 0x7b, 0x20, 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x73, 0x74, 0x61, - 0x74, 0x3f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x3d, 0x65, 0x62, 0x70, 0x66, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x6c, 0x69, 0x73, 0x74, 0x27, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, - 0x27, 0x47, 0x45, 0x54, 0x27, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x3a, - 0x20, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x27, 0x2c, 0x20, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x3a, 0x20, - 0x66, 0x61, 0x6c, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, - 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, - 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x3d, - 0x27, 0x3c, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, - 0x30, 0x30, 0x25, 0x22, 0x3e, 0x3c, 0x74, 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, - 0x65, 0x66, 0x74, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x2d, 0x62, - 0x61, 0x73, 0x65, 0x64, 0x20, 0x64, 0x79, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, - 0x20, 0x6e, 0x65, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, - 0x3e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, - 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3c, 0x2f, 0x74, 0x68, - 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x27, 0x3b, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x76, 0x61, 0x72, 0x20, 0x67, 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x3d, 0x66, 0x61, 0x6c, 0x73, - 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x64, - 0x61, 0x74, 0x61, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, - 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, - 0x75, 0x77, 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x28, 0x22, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, - 0x64, 0x3e, 0x22, 0x2b, 0x61, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, - 0x22, 0x2b, 0x62, 0x2e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x2b, 0x22, 0x3c, 0x2f, 0x74, - 0x64, 0x3e, 0x3c, 0x74, 0x64, 0x3e, 0x22, 0x2b, 0x62, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, - 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x22, 0x29, 0x3b, 0x0a, - 0x09, 0x09, 0x09, 0x20, 0x67, 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x3d, 0x74, 0x72, 0x75, 0x65, + 0x20, 0x20, 0x67, 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x09, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x69, 0x66, 0x28, 0x21, 0x67, 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x6f, 0x75, 0x77, 0x20, 0x3d, 0x20, - 0x62, 0x6f, 0x75, 0x77, 0x20, 0x2b, 0x20, 0x27, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x20, - 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x22, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x20, 0x63, - 0x6f, 0x6c, 0x73, 0x70, 0x61, 0x6e, 0x3d, 0x22, 0x34, 0x22, 0x3e, 0x3c, 0x66, 0x6f, 0x6e, 0x74, - 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3d, 0x22, 0x23, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x22, - 0x3e, 0x4e, 0x6f, 0x20, 0x65, 0x42, 0x50, 0x46, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x20, - 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x3c, 0x2f, 0x66, 0x6f, 0x6e, 0x74, 0x3e, 0x3c, 0x2f, 0x74, - 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x27, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x62, 0x6f, 0x75, 0x77, 0x3d, 0x62, 0x6f, 0x75, 0x77, 0x2b, 0x22, 0x3c, 0x2f, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x3e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x65, - 0x62, 0x70, 0x66, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, - 0x62, 0x6f, 0x75, 0x77, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x28, 0x21, 0x67, 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x29, 0x20, 0x7b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x28, 0x24, 0x28, + 0x27, 0x3c, 0x74, 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x22, + 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x20, 0x63, 0x6f, 0x6c, 0x73, 0x70, 0x61, 0x6e, 0x3d, + 0x22, 0x34, 0x22, 0x3e, 0x3c, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3d, + 0x22, 0x23, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x22, 0x3e, 0x4e, 0x6f, 0x20, 0x64, 0x79, 0x6e, + 0x61, 0x6d, 0x69, 0x63, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x20, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x3c, 0x2f, 0x66, 0x6f, 0x6e, 0x74, 0x3e, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, + 0x74, 0x72, 0x3e, 0x27, 0x29, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x64, 0x79, 0x6e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x22, 0x29, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x28, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x24, - 0x28, 0x22, 0x23, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x31, 0x22, 0x29, 0x2e, 0x63, 0x6c, 0x69, - 0x63, 0x6b, 0x28, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x69, 0x6e, 0x67, 0x42, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x24, 0x28, 0x22, 0x23, 0x66, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x32, 0x22, 0x29, 0x2e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x28, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x69, 0x6e, 0x67, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x73, - 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x28, 0x29, - 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x28, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x31, 0x30, 0x30, 0x30, 0x29, 0x3b, - 0x0a, 0x7d, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, 0x2e, 0x61, 0x6a, 0x61, 0x78, 0x28, 0x7b, 0x20, + 0x75, 0x72, 0x6c, 0x3a, 0x20, 0x27, 0x6a, 0x73, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x74, 0x3f, 0x63, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x3d, 0x65, 0x62, 0x70, 0x66, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x6c, 0x69, 0x73, 0x74, 0x27, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x47, 0x45, + 0x54, 0x27, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x27, 0x6a, + 0x73, 0x6f, 0x6e, 0x27, 0x2c, 0x20, 0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x3a, 0x20, 0x66, 0x61, 0x6c, + 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3a, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x24, 0x28, 0x27, 0x3c, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x20, 0x77, 0x69, 0x64, 0x74, 0x68, 0x3d, 0x22, 0x31, 0x30, 0x30, 0x25, 0x22, 0x3e, 0x3c, 0x74, + 0x72, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x6c, 0x65, 0x66, 0x74, 0x3e, 0x3c, 0x74, 0x68, + 0x3e, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x64, 0x20, 0x64, 0x79, + 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x20, 0x6e, 0x65, 0x74, 0x6d, 0x61, 0x73, + 0x6b, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x74, 0x68, 0x3e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3c, 0x2f, 0x74, 0x68, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x27, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x67, 0x6f, 0x74, 0x73, + 0x6f, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x24, 0x2e, 0x65, 0x61, 0x63, 0x68, 0x28, 0x64, 0x61, 0x74, 0x61, 0x2c, 0x20, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x61, 0x2c, 0x62, 0x29, 0x20, 0x7b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x72, 0x6f, 0x77, 0x20, + 0x3d, 0x20, 0x24, 0x28, 0x27, 0x3c, 0x74, 0x72, 0x2f, 0x3e, 0x27, 0x29, 0x3b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, + 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x61, 0x29, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, + 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x2e, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x65, 0x6c, 0x6c, 0x54, 0x6f, 0x52, + 0x6f, 0x77, 0x28, 0x72, 0x6f, 0x77, 0x2c, 0x20, 0x62, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x28, + 0x72, 0x6f, 0x77, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x67, + 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x29, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, + 0x20, 0x28, 0x21, 0x67, 0x6f, 0x74, 0x73, 0x6f, 0x6d, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x28, 0x24, 0x28, 0x27, 0x3c, 0x74, + 0x72, 0x3e, 0x3c, 0x74, 0x64, 0x20, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x3d, 0x22, 0x63, 0x65, 0x6e, + 0x74, 0x65, 0x72, 0x22, 0x20, 0x63, 0x6f, 0x6c, 0x73, 0x70, 0x61, 0x6e, 0x3d, 0x22, 0x34, 0x22, + 0x3e, 0x3c, 0x66, 0x6f, 0x6e, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3d, 0x22, 0x23, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x22, 0x3e, 0x4e, 0x6f, 0x20, 0x65, 0x42, 0x50, 0x46, 0x20, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x20, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x3c, 0x2f, 0x66, 0x6f, + 0x6e, 0x74, 0x3e, 0x3c, 0x2f, 0x74, 0x64, 0x3e, 0x3c, 0x2f, 0x74, 0x72, 0x3e, 0x27, 0x29, 0x29, + 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x24, + 0x28, 0x22, 0x23, 0x65, 0x62, 0x70, 0x66, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x29, 0x2e, 0x68, + 0x74, 0x6d, 0x6c, 0x28, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7d, 0x7d, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x3b, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x28, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x28, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x31, 0x30, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x7d, 0x29, + 0x3b, 0x0a, }; static const unsigned char gpowerdns_logo_220px_pngData[] = { diff -Nru dnsdist-1.9.10/iputils.hh dnsdist-1.9.14/iputils.hh --- dnsdist-1.9.10/iputils.hh 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/iputils.hh 2026-04-22 16:47:03.000000000 +0000 @@ -268,9 +268,10 @@ [[nodiscard]] bool isUnspecified() const { - const ComboAddress unspecifiedV4("0.0.0.0:0"); - const ComboAddress unspecifiedV6("[::]:0"); - return *this == unspecifiedV4 || *this == unspecifiedV6; + static const ComboAddress unspecifiedV4("0.0.0.0:0"); + static const ComboAddress unspecifiedV6("[::]:0"); + const auto compare = ComboAddress::addressOnlyEqual(); + return compare(*this, unspecifiedV4) || compare(*this, unspecifiedV6); } ComboAddress mapToIPv4() const diff -Nru dnsdist-1.9.10/libssl.cc dnsdist-1.9.14/libssl.cc --- dnsdist-1.9.10/libssl.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/libssl.cc 2026-04-22 16:47:03.000000000 +0000 @@ -372,14 +372,23 @@ return SSL_TLSEXT_ERR_NOACK; } + const auto ocsp_resp_size = data->second.size(); + /* the behaviour is alas different in 3.6.0 because of a regression introduced in b1b4b154fd389ac6254d49cfb11aee36c1c51b84: + the value passed to SSL_set_tlsext_status_ocsp_resp() is not freed in 3.6.0 as it is in all other OpenSSL versions. + See https://github.com/openssl/openssl/issues/28888 */ +#if OPENSSL_VERSION_NUMBER != 0x30600000L /* we need to allocate a copy because OpenSSL will free the pointer passed to SSL_set_tlsext_status_ocsp_resp() */ - void* copy = OPENSSL_malloc(data->second.size()); - if (copy == nullptr) { + void* ocsp_resp = OPENSSL_malloc(ocsp_resp_size); + if (ocsp_resp == nullptr) { return SSL_TLSEXT_ERR_NOACK; } - memcpy(copy, data->second.data(), data->second.size()); - SSL_set_tlsext_status_ocsp_resp(ssl, copy, data->second.size()); + memcpy(ocsp_resp, data->second.data(), ocsp_resp_size); +#else + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): the parameter is not freed in this version (3.6.0, see above) but the parameter is not marked const. + void* ocsp_resp = const_cast(data->second.data()); +#endif + SSL_set_tlsext_status_ocsp_resp(ssl, ocsp_resp, ocsp_resp_size); return SSL_TLSEXT_ERR_OK; } diff -Nru dnsdist-1.9.10/statnode.cc dnsdist-1.9.14/statnode.cc --- dnsdist-1.9.10/statnode.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/statnode.cc 2026-04-22 16:47:03.000000000 +0000 @@ -22,10 +22,10 @@ childstat=child.second.print(depth+8, childstat, silent || children.size()>1024); } if(!silent || children.size()>1) - cout< +/* www.powerdns.com. -> . <- fullnames com. powerdns.com - www.powerdns.com. + www.powerdns.com. */ -void StatNode::submit(std::vector::const_iterator end, std::vector::const_iterator begin, const std::string& domain, int rcode, unsigned int bytes, const boost::optional& remote, unsigned int count, bool hit) +void StatNode::submit(std::vector::const_iterator end, std::vector::const_iterator begin, const DNSName& domain, int rcode, unsigned int bytes, const boost::optional& remote, unsigned int count, bool hit) { // cerr<<"Submit called for domain='"<& remote); @@ -75,5 +75,5 @@ children_t children; private: - void submit(std::vector::const_iterator end, std::vector::const_iterator begin, const std::string& domain, int rcode, unsigned int bytes, const boost::optional& remote, unsigned int count, bool hit); + void submit(std::vector::const_iterator end, std::vector::const_iterator begin, const DNSName& domain, int rcode, unsigned int bytes, const boost::optional& remote, unsigned int count, bool hit); }; diff -Nru dnsdist-1.9.10/test-dnsdist-dnsparser.cc dnsdist-1.9.14/test-dnsdist-dnsparser.cc --- dnsdist-1.9.10/test-dnsdist-dnsparser.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/test-dnsdist-dnsparser.cc 2026-04-22 16:47:03.000000000 +0000 @@ -386,6 +386,7 @@ BOOST_AUTO_TEST_CASE(test_Overlay) { const DNSName target("powerdns.com."); + const DNSName notTheTarget("not-powerdns.com."); { PacketBuffer response; @@ -460,6 +461,23 @@ lastOffset = record.d_contentOffset + record.d_contentLength; } } + + { + /* response with overflowing CNAME record */ + PacketBuffer response; + GenericDNSPacketWriter pwR(response, target, QType::A, QClass::IN, 0); + pwR.getHeader()->qr = 1; + pwR.getHeader()->rd = 1; + pwR.getHeader()->ra = 1; + pwR.getHeader()->id = htons(42); + pwR.startRecord(target, QType::CNAME, 7200, QClass::IN, DNSResourceRecord::ANSWER); + pwR.xfrName(notTheTarget); + pwR.commit(); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): this is the API we have + auto packet = std::string_view(reinterpret_cast(response.data()), response.size() - 1U); + BOOST_CHECK_THROW(dnsdist::DNSPacketOverlay overlay(packet), std::runtime_error); + } } BOOST_AUTO_TEST_SUITE_END(); diff -Nru dnsdist-1.9.10/test-dnsdist-lua-ffi.cc dnsdist-1.9.14/test-dnsdist-lua-ffi.cc --- dnsdist-1.9.10/test-dnsdist-lua-ffi.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/test-dnsdist-lua-ffi.cc 2026-04-22 16:47:03.000000000 +0000 @@ -1027,4 +1027,54 @@ dnsdist_ffi_svc_record_parameters_free(parameters); } +BOOST_AUTO_TEST_CASE(test_set_altername_name) +{ + const DNSName initialQName("www.powerdns.com."); + InternalQueryState ids; + ids.origRemote = ComboAddress("192.0.2.1:4242"); + ids.origDest = ComboAddress("192.0.2.255:53"); + ids.qtype = QType::A; + ids.qclass = QClass::IN; + ids.protocol = dnsdist::Protocol::DoUDP; + ids.qname = initialQName; + ids.queryRealTime.start(); + PacketBuffer query; + GenericDNSPacketWriter pwQ(query, ids.qname, QType::A, QClass::IN, 0); + pwQ.getHeader()->rd = 1; + pwQ.getHeader()->id = htons(42); + + DNSQuestion dnsQuestion(ids, query); + dnsdist_ffi_dnsquestion_t lightDQ(&dnsQuestion); + + { + /* check invalid parameters */ + dnsdist_ffi_dnsquestion_set_alternate_name(nullptr, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0); + dnsdist_ffi_dnsquestion_set_alternate_name(&lightDQ, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0); + dnsdist_ffi_dnsquestion_set_alternate_name(&lightDQ, "alternate", 0, nullptr, 0, nullptr, 0, nullptr, 0); + } + + const std::string tag("alternate-name-tag"); + const std::string tagValue("alternate-name-tag-value"); + const std::string formerTagName("alternate-name-former-value"); + const DNSName target("new.target.net."); + BOOST_REQUIRE(dnsdist_ffi_dnsquestion_set_alternate_name(&lightDQ, target.getStorage().data(), target.getStorage().size(), tag.data(), tag.size(), tagValue.data(), tagValue.size(), formerTagName.data(), formerTagName.size())); + + BOOST_CHECK_EQUAL(ids.qname.toString(), target.toString()); + BOOST_CHECK_EQUAL(ids.skipCache, true); + BOOST_REQUIRE(ids.qTag != nullptr); + BOOST_CHECK_EQUAL(ids.qTag->at(tag), tagValue); + BOOST_CHECK_EQUAL(ids.qTag->at(formerTagName), std::string(initialQName.getStorage())); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + MOADNSParser mdp(false, reinterpret_cast(dnsQuestion.getData().data()), dnsQuestion.getData().size()); + BOOST_CHECK_EQUAL(mdp.d_qname, target); + BOOST_CHECK_EQUAL(mdp.d_header.qdcount, 1U); + + /* also check that we can rename without providing any tags */ + BOOST_CHECK(dnsdist_ffi_dnsquestion_set_alternate_name(&lightDQ, target.getStorage().data(), target.getStorage().size(), nullptr, 0, nullptr, 0, nullptr, 0)); + + /* and that we can pass a tag name without a value */ + BOOST_CHECK(dnsdist_ffi_dnsquestion_set_alternate_name(&lightDQ, target.getStorage().data(), target.getStorage().size(), tag.data(), tag.size(), nullptr, 0, nullptr, 0)); +} + BOOST_AUTO_TEST_SUITE_END(); diff -Nru dnsdist-1.9.10/test-dnsdistasync.cc dnsdist-1.9.14/test-dnsdistasync.cc --- dnsdist-1.9.10/test-dnsdistasync.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/test-dnsdistasync.cc 2026-04-22 16:47:03.000000000 +0000 @@ -110,6 +110,38 @@ holder->stop(); } +BOOST_AUTO_TEST_CASE(test_Collison) +{ + auto holder = std::make_unique(); + + { + uint16_t asyncID = 1; + uint16_t queryID = 42; + timeval ttd{}; + gettimeofday(&ttd, nullptr); + // timeout in 100 ms + const timeval add{0, 100000}; + ttd = ttd + add; + + holder->push(asyncID, queryID, ttd, std::make_unique()); + BOOST_CHECK(!holder->empty()); + + holder->push(asyncID, queryID, ttd, std::make_unique()); + + auto query = holder->get(asyncID, queryID); + BOOST_CHECK(holder->empty()); + + query = holder->get(asyncID, queryID); + BOOST_CHECK(query == nullptr); + + // sleep for 200 ms, to be sure the main thread has + // been awakened + usleep(200000); + } + + holder->stop(); +} + BOOST_AUTO_TEST_CASE(test_TimeoutFailClose) { auto holder = std::make_unique(false); diff -Nru dnsdist-1.9.10/test-dnsdistnghttp2_cc.cc dnsdist-1.9.14/test-dnsdistnghttp2_cc.cc --- dnsdist-1.9.10/test-dnsdistnghttp2_cc.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/test-dnsdistnghttp2_cc.cc 2026-04-22 16:47:03.000000000 +0000 @@ -186,9 +186,6 @@ int rv = nghttp2_submit_response(d_session.get(), streamId, hdrs, sizeof(hdrs) / sizeof(*hdrs), &dataProvider); // cerr<<"Submitting response for stream ID "<(query.data()), query.size(), sizeof(dnsheader), false); if (qname == DNSName("goaway.powerdns.com.")) { - conn->submitGoAway(); + conn->submitGoAway(false); } else if (qname == DNSName("500.powerdns.com.") && (id % 2) == 0) { /* we return a 500 on the first query only */ @@ -613,7 +609,7 @@ }}, /* acknowledge settings */ {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits::max(), [](int desc) { - s_connectionBuffers.at(desc)->submitGoAway(); + s_connectionBuffers.at(desc)->submitGoAway(true); dynamic_cast(s_mplexer.get())->setReady(desc); }}, {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits::max()}, @@ -697,7 +693,7 @@ {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits::max()}, /* acknowledge settings */ {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits::max(), [](int desc) { - s_connectionBuffers.at(desc)->submitGoAway(); + s_connectionBuffers.at(desc)->submitGoAway(true); dynamic_cast(s_mplexer.get())->setReady(desc); }}, {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits::max()}, @@ -794,7 +790,7 @@ {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits::max()}, /* later the backend sends a go away frame */ {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits::max(), [](int desc) { - s_connectionBuffers.at(desc)->submitGoAway(); + s_connectionBuffers.at(desc)->submitGoAway(true); }}, {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, }; @@ -893,7 +889,7 @@ {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits::max()}, /* try to read, the backend says to go away */ {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits::max(), [](int desc) { - s_connectionBuffers.at(desc)->submitGoAway(); + s_connectionBuffers.at(desc)->submitGoAway(true); }}, {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, }; diff -Nru dnsdist-1.9.10/test-iputils_hh.cc dnsdist-1.9.14/test-iputils_hh.cc --- dnsdist-1.9.10/test-iputils_hh.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/test-iputils_hh.cc 2026-04-22 16:47:03.000000000 +0000 @@ -895,4 +895,28 @@ } } +BOOST_AUTO_TEST_CASE(test_unspecified) +{ + struct TestParams + { + std::string str; + bool unspecified; + }; + const std::array tests{{ + {"0.0.0.0:0", true}, + {"[::]:0", true}, + {"0.0.0.0:853", true}, + {"[::]:853", true}, + {"192.0.2.1:0", false}, + {"192.0.2.1:853", false}, + {"[2001:db8::1]:0", false}, + {"[2001:db8::1]:853", false} + }}; + + for (const auto& test : tests) { + const ComboAddress address(test.str); + BOOST_CHECK_EQUAL(address.isUnspecified(), test.unspecified); + } +} + BOOST_AUTO_TEST_SUITE_END() diff -Nru dnsdist-1.9.10/xsk.cc dnsdist-1.9.14/xsk.cc --- dnsdist-1.9.10/xsk.cc 2025-05-20 09:13:25.000000000 +0000 +++ dnsdist-1.9.14/xsk.cc 2026-04-22 16:47:03.000000000 +0000 @@ -709,7 +709,7 @@ size_t XskPacket::getCapacity() const noexcept { - return frameSize; + return frameSize - getDataOffset(); } void XskPacket::changeDirectAndUpdateChecksum() noexcept