]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #7807 from ahupowerdns/lmdb-sync-mode
authorPeter van Dijk <peter.van.dijk@powerdns.com>
Fri, 7 Jun 2019 15:26:26 +0000 (17:26 +0200)
committerGitHub <noreply@github.com>
Fri, 7 Jun 2019 15:26:26 +0000 (17:26 +0200)
Make explicit lmdbbackend synchronous option

155 files changed:
.circleci/config.yml
.gitignore
builder
builder-support/dockerfiles/Dockerfile.target.sdist
builder-support/gen-version
configure.ac
docs/backends/bind.rst
docs/backends/generic-mysql.rst
docs/backends/generic-sqlite3.rst
docs/backends/geoip.rst
docs/backends/index.rst
docs/backends/lmdb.rst [new file with mode: 0644]
docs/common/api/zone.rst
docs/dnssec/profile.rst
docs/http-api/swagger/authoritative-api-swagger.yaml
docs/manpages/sdig.1.rst
docs/modes-of-operation.rst
docs/secpoll.zone
docs/settings.rst
docs/upgrading.rst
m4/boost.m4
m4/pdns_check_libcurl.m4 [new file with mode: 0644]
m4/pdns_check_os.m4
m4/pdns_enable_tools.m4 [new file with mode: 0644]
m4/pdns_with_lua_records.m4
m4/pdns_with_net_snmp.m4
modules/bindbackend/bindbackend2.cc
modules/bindbackend/bindbackend2.hh
modules/bindbackend/binddnssec.cc
modules/gmysqlbackend/gmysqlbackend.cc
modules/godbcbackend/godbcbackend.cc
modules/gpgsqlbackend/gpgsqlbackend.cc
modules/gsqlite3backend/gsqlite3backend.cc
modules/ldapbackend/native.cc
modules/lmdbbackend/lmdb-safe.hh
modules/lmdbbackend/lmdbbackend.cc
pdns/Makefile.am
pdns/backends/gsql/gsqlbackend.cc
pdns/common_startup.cc
pdns/common_startup.hh
pdns/communicator.cc
pdns/dns_random.cc
pdns/dnsbulktest.cc
pdns/dnsdist-console.cc
pdns/dnsdist-lua-actions.cc
pdns/dnsdist-lua-bindings-dnsquestion.cc
pdns/dnsdist-lua-rules.cc
pdns/dnsdist-lua-vars.cc
pdns/dnsdist-tcp.cc
pdns/dnsdist-web.cc
pdns/dnsdist.cc
pdns/dnsdist.hh
pdns/dnsdistdist/dnsdist-rules.hh
pdns/dnsdistdist/docs/changelog.rst
pdns/dnsdistdist/docs/reference/config.rst
pdns/dnsdistdist/docs/reference/dq.rst
pdns/dnsdistdist/docs/rules-actions.rst
pdns/dnsdistdist/docs/statistics.rst
pdns/dnsdistdist/doh.cc
pdns/dnsdistdist/libssl.cc
pdns/dnsdistdist/m4/pdns_check_libh2o_evloop.m4
pdns/dnsdistdist/tcpiohandler.cc
pdns/dnspacket.cc
pdns/dnspacket.hh
pdns/dnsparser.cc
pdns/dnspcap.cc
pdns/dnsrecords.cc
pdns/dnsseckeeper.hh
pdns/dnstap.cc
pdns/dnstcpbench.cc
pdns/dumresp.cc
pdns/dynlistener.cc
pdns/fstrm_logger.cc
pdns/fstrm_logger.hh
pdns/iputils.cc
pdns/iputils.hh
pdns/ixfrutils.cc
pdns/lua-record.cc
pdns/lwres.cc
pdns/lwres.hh
pdns/minicurl.cc
pdns/minicurl.hh
pdns/nsec3dig.cc
pdns/nsecrecords.cc
pdns/packethandler.cc
pdns/packethandler.hh
pdns/pdns_recursor.cc
pdns/pdnsutil.cc
pdns/qtype.hh
pdns/rec-dnstap.hh [new file with mode: 0644]
pdns/rec-lua-conf.cc
pdns/rec-lua-conf.hh
pdns/receiver.cc
pdns/recursordist/.gitignore
pdns/recursordist/Makefile.am
pdns/recursordist/configure.ac
pdns/recursordist/dnstap.cc [new symlink]
pdns/recursordist/dnstap.hh [new symlink]
pdns/recursordist/dnstap.proto [new symlink]
pdns/recursordist/docs/changelog/4.1.rst
pdns/recursordist/docs/changelog/4.2.rst
pdns/recursordist/docs/lua-config/protobuf.rst
pdns/recursordist/docs/nod_udr.rst [new file with mode: 0644]
pdns/recursordist/docs/security.rst
pdns/recursordist/fstrm_logger.cc [new symlink]
pdns/recursordist/fstrm_logger.hh [new symlink]
pdns/recursordist/html/index.html
pdns/recursordist/html/local.js
pdns/recursordist/m4/pdns_check_dnstap.m4 [new symlink]
pdns/recursordist/rec-dnstap.hh [new symlink]
pdns/recursordist/test-syncres_cc.cc
pdns/rfc2136handler.cc
pdns/saxfr.cc
pdns/sdig.cc
pdns/serialtweaker.cc
pdns/ssqlite3.cc
pdns/ssqlite3.hh
pdns/stubquery.cc
pdns/syncres.cc
pdns/syncres.hh
pdns/tcpiohandler.hh
pdns/tcpreceiver.cc
pdns/toysdig.cc
pdns/version.cc
pdns/webserver.cc
pdns/webserver.hh
pdns/ws-auth.cc
pdns/ws-recursor.cc
regression-tests.api/runtests.py
regression-tests.api/test_Basics.py
regression-tests.api/test_Servers.py
regression-tests.api/test_Zones.py
regression-tests.api/test_helper.py
regression-tests.auth-py/authtests.py
regression-tests.auth-py/test_ALIAS.py
regression-tests.auth-py/test_LuaRecords.py
regression-tests.dnsdist/configCA.conf
regression-tests.dnsdist/configServer.conf
regression-tests.dnsdist/runtests
regression-tests.dnsdist/test_API.py
regression-tests.dnsdist/test_DOH.py
regression-tests.dnsdist/test_TLS.py
regression-tests.nobackend/soa-edit/command
regression-tests.nobackend/soa-edit/expected_result
regression-tests.recursor-dnssec/runtests
regression-tests.recursor-dnssec/test_RecDnstap.py [new file with mode: 0644]
regression-tests/README.md
regression-tests/runtests
regression-tests/start-test-stop
regression-tests/tests/1dyndns-cname-and-other-data/command
regression-tests/tests/1dyndns-cname-and-other-data/expected_result
regression-tests/tests/too-big-for-udp-query-always-truncate-additional/command [moved from regression-tests/tests/too-big-for-udp-query-no-truncate-additional/command with 100% similarity]
regression-tests/tests/too-big-for-udp-query-always-truncate-additional/description [moved from regression-tests/tests/too-big-for-udp-query-no-truncate-additional/description with 53% similarity]
regression-tests/tests/too-big-for-udp-query-always-truncate-additional/expected_result [new file with mode: 0644]
regression-tests/tests/too-big-for-udp-query-no-truncate-additional/expected_result [deleted file]

index 1f86d52ead003b01a94b97d753fff3935b6d4fd7..24e4874a4abd069bbb2717798767018627b88c40 100644 (file)
@@ -12,7 +12,26 @@ commands:
           command: mkdir -p $HOME/.ssh && ssh-keyscan $(echo ${CIRCLE_REPOSITORY_URL} | sed 's,.*@\([^:]*\):.*,\1,') > $HOME/.ssh/known_hosts
       - run:
           name: Clone the repo
-          command: git clone --depth 1 --branch $CIRCLE_BRANCH $CIRCLE_REPOSITORY_URL ~/project
+          command: |
+            if [ -n "$CIRCLE_PR_NUMBER" ]
+            then
+              echo === Checking out PR "$CIRCLE_PR_NUMBER" from "$CIRCLE_REPOSITORY_URL"
+              git clone --depth 1 $CIRCLE_REPOSITORY_URL ~/project
+              cd ~/project
+              git fetch --depth 1 origin +refs/pull/${CIRCLE_PR_NUMBER}/merge
+              git checkout -qf FETCH_HEAD
+            else
+              echo === Checking out branch "${CIRCLE_BRANCH}" from "$CIRCLE_REPOSITORY_URL"
+              git clone --depth 1 --branch $CIRCLE_BRANCH $CIRCLE_REPOSITORY_URL ~/project
+            fi
+            git show -s
+
+  install-coverity-tools:
+    description: Install the coverity tools to /usr/local
+    steps:
+      - run:
+          name: Install Coverity tools
+          command: curl -s https://scan.coverity.com/download/linux64 --data "token=${COVERITY_TOKEN}&project=${COVERITY_PROJECT}" |  gunzip | tar xvf /dev/stdin --strip-components=2 --no-same-owner -C /usr/local
 
   auth-regress-setup:
     description: Prepare the environment for auth regression tests
@@ -155,6 +174,8 @@ commands:
               autoconf \
               automake \
               bison \
+              bzip2 \
+              curl \
               default-libmysqlclient-dev \
               flex \
               g++ \
@@ -617,6 +638,7 @@ jobs:
             CXXFLAGS="-O1 -Werror=vla" \
             ./configure \
             --enable-unit-tests \
+            --enable-nod \
             --prefix=/opt/pdns-recursor \
             --with-libsodium \
             --with-lua=luajit \
@@ -711,8 +733,206 @@ jobs:
             PDNSRECURSOR="/opt/pdns-recursor/sbin/pdns_recursor" \
             ./runtests recursor
 
+  coverity-auth:
+    docker:
+      - image: debian:stretch
+    steps:
+      - install-auth-dev-deps
+      - install-coverity-tools
+      - checkout-shallow
+      - run:
+          name: autoconf
+          command: BUILDER_VERSION=0.0.0-git1 autoreconf -vfi
+      - run:
+          name: configure
+          command: |
+            CFLAGS="-O1 -Werror=vla" \
+            CXXFLAGS="-O1 -Werror=vla" \
+            ./configure \
+              --disable-lua-records \
+              --with-modules='bind lmdb ldap gmysql gsqlite3 gpgsql godbc mydns random tinydns' \
+              --enable-tools \
+              --with-lmdb=/usr \
+              --with-libsodium \
+              --prefix=/opt/pdns-auth
+      - run:
+          name: build
+          command: /usr/local/bin/cov-build --dir cov-int make -j2 -k
+      - run:
+          name: Create Coverity tarball
+          command: tar caf auth.tar.bz2 cov-int
+      - run:
+          name: Upload tarball to coverity
+          command: |
+            curl --form token=${COVERITY_TOKEN} \
+            --form email="${COVERITY_EMAIL}" \
+            --form file=@auth.tar.bz2 \
+            --form version="$(./builder-support/gen-version)" \
+            --form description="master build" \
+            https://scan.coverity.com/builds?project=${COVERITY_PROJECT}
+
+  coverity-dnsdist:
+    docker:
+      - image: debian:stretch
+    steps:
+      - run:
+          name: Install dependencies
+          command: |
+            apt-get update && apt-get -qq --no-install-recommends install \
+            autoconf \
+            automake \
+            bison \
+            bzip2 \
+            ca-certificates \
+            curl \
+            flex \
+            g++ \
+            git \
+            libboost-all-dev \
+            libcap-dev \
+            libedit-dev \
+            libfstrm-dev \
+            libluajit-5.1-dev \
+            libprotobuf-dev \
+            libre2-dev \
+            libsnmp-dev \
+            libsodium-dev \
+            libssl-dev \
+            libsystemd-dev \
+            libtool \
+            make \
+            pkg-config \
+            protobuf-compiler \
+            ragel \
+            virtualenv
+      - install-coverity-tools
+      - checkout-shallow
+      - run:
+          name: autoconf
+          command: BUILDER_VERSION=0.0.0-git1 autoreconf -vfi
+          working_directory: ~/project/pdns/dnsdistdist
+      - run:
+          name: configure
+          command: |
+            CFLAGS="-O1 -Werror=vla" \
+            CXXFLAGS="-O1 -Werror=vla" \
+            ./configure \
+            --disable-systemd \
+            --disable-unit-tests \
+            --enable-dnstap \
+            --enable-dnscrypt \
+            --enable-dns-over-tls \
+            --prefix=/opt/dnsdist \
+            --with-libsodium \
+            --with-lua=luajit \
+            --with-libcap \
+            --with-protobuf=yes \
+            --with-re2
+          working_directory: ~/project/pdns/dnsdistdist
+      - run:
+          name: build
+          command: /usr/local/bin/cov-build --dir cov-int make -j2 -k
+          working_directory: ~/project/pdns/dnsdistdist
+      - run:
+          name: Create Coverity tarball
+          command: tar caf dnsdist.tar.bz2 cov-int
+          working_directory: ~/project/pdns/dnsdistdist
+      - run:
+          name: Upload tarball to coverity
+          command: |
+            curl --form token=${COVERITY_TOKEN} \
+            --form email="${COVERITY_EMAIL}" \
+            --form file=@pdns/dnsdistdist/dnsdist.tar.bz2 \
+            --form version="$(./builder-support/gen-version)" \
+            --form description="master build" \
+            https://scan.coverity.com/builds?project=${COVERITY_PROJECT}
+
+  coverity-recursor:
+    docker:
+      - image: debian:stretch
+    steps:
+      - run:
+          name: Install dependencies
+          command: |
+            apt-get update && apt-get -qq --no-install-recommends install \
+            autoconf \
+            automake \
+            ca-certificates \
+            curl \
+            bison \
+            bzip2 \
+            flex \
+            g++ \
+            git \
+            libboost-all-dev \
+            libcap-dev \
+            libluajit-5.1-dev \
+            libprotobuf-dev \
+            libsodium-dev \
+            libssl-dev \
+            libsystemd-dev \
+            libtool \
+            make \
+            pkg-config \
+            protobuf-compiler \
+            ragel \
+            virtualenv
+      - install-coverity-tools
+      - checkout-shallow
+      - run:
+          name: autoconf
+          command: BUILDER_VERSION=0.0.0-git1 autoreconf -vfi
+          working_directory: ~/project/pdns/recursordist
+      - run:
+          name: configure
+          command: |
+            CFLAGS="-O1 -Werror=vla" \
+            CXXFLAGS="-O1 -Werror=vla" \
+            ./configure \
+            --disable-systemd \
+            --disable-unit-tests \
+            --prefix=/opt/pdns-recursor \
+            --with-libsodium \
+            --with-lua=luajit \
+            --with-libcap \
+            --with-protobuf=yes \
+            --without-net-snmp
+          working_directory: ~/project/pdns/recursordist
+      - run:
+          name: build
+          command: /usr/local/bin/cov-build --dir cov-int make -j2 -k
+          working_directory: ~/project/pdns/recursordist
+      - run:
+          name: Create Coverity tarball
+          command: tar caf recursor.tar.bz2 cov-int
+          working_directory: ~/project/pdns/recursordist
+      - run:
+          name: Upload tarball to coverity
+          command: |
+            curl --form token=${COVERITY_TOKEN} \
+            --form email="${COVERITY_EMAIL}" \
+            --form file=@pdns/recursordist/recursor.tar.bz2 \
+            --form version="$(./builder-support/gen-version)" \
+            --form description="master build" \
+            https://scan.coverity.com/builds?project=${COVERITY_PROJECT}
+
 workflows:
   version: 2
+  coverity:
+    triggers:
+      - schedule:
+          cron: "0 0 * * *"
+          filters:
+            branches:
+              only: master
+    jobs:
+      - coverity-auth:
+          context: auth-coverity
+      - coverity-dnsdist:
+          context: dnsdist-coverity
+      - coverity-recursor:
+          context: recursor-coverity
+
   build-and-test-all:
     jobs:
       - build-auth
index 3823101c7182f87147f1ffb4aeda15b5918b24a8..0b691a4b0147cb21dbf76cb2755a32dab290752c 100644 (file)
@@ -50,3 +50,6 @@ pdns_recursor.pid
 *.gcno
 *.gcov
 built_pkgs
+*-wal
+*-shm
+__pycache__
\ No newline at end of file
diff --git a/builder b/builder
index 4f5ab935098ebacd6b55e89b405bd69e80ab2baf..6176c5f68354ca82814ef20a4c87327785157ff3 160000 (submodule)
--- a/builder
+++ b/builder
@@ -1 +1 @@
-Subproject commit 4f5ab935098ebacd6b55e89b405bd69e80ab2baf
+Subproject commit 6176c5f68354ca82814ef20a4c87327785157ff3
index 81760329a7471d1853a7acd6e66cfe700a20e52f..57c09725c3e262858c36fbd0220dda780a757a0b 100644 (file)
 FROM alpine:3.6 as sdist
 ARG BUILDER_CACHE_BUSTER=
 
+@IF [ -z "$M_all$M_authoritative$M_recursor$M_dnsdist"]
+RUN echo "no valid modules specified!" ; exit 1
+@ENDIF
+
 @IF [ ! -z "$M_authoritative$M_all" ]
 COPY --from=pdns-authoritative /sdist/ /sdist/
 @ENDIF
index a3eb581319eb1b4ad14b071ca3743dbd4273e6bb..4411794d7244775ea3ff958d686fabe49db21d1a 100755 (executable)
@@ -10,7 +10,7 @@ DIRTY=""
 git status | grep -q clean || DIRTY='.dirty'
 
 # Special environment variable to signal that we are building a release, as this
-# has condequenses for the version number.
+# has consequenses for the version number.
 if [ "${IS_RELEASE}" = "YES" ]; then
   TAG="$(git describe --tags --exact-match 2> /dev/null | cut -d- -f 2-)"
   if [ -n "${TAG}" ]; then
@@ -27,17 +27,26 @@ fi
 # Generate the version number based on the branch
 #
 if [ ! -z "$(git rev-parse --abbrev-ref HEAD 2> /dev/null)" ]; then
-  GIT_VERSION=""
-  if $(git rev-parse --abbrev-ref HEAD | grep -q 'rel/'); then
-    REL_TYPE="$(git rev-parse --abbrev-ref HEAD | cut -d/ -f 2 | cut -d- -f 1)"
-    GIT_VERSION="$(git describe --match=${REL_TYPE}-* --tags | cut -d- -f2-)"
+  if [ -n "${BUILDER_MODULES}" ]; then
+    match=${BUILDER_MODULES}
+    [ $match = "authoritative" ] && match='auth'
+    [ $match = "recursor" ] && match='rec'
+    GIT_VERSION="$(git describe --match=${match}-* --tags | cut -d- -f2-)"
+    if [ $(echo ${GIT_VERSION} | awk -F"-" '{print NF-1}') = '3' ]; then
+      # A prerelease happened before
+      LAST_TAG="$(echo ${GIT_VERSION} | cut -d- -f1-2)"
+      COMMITS_SINCE_TAG="$(echo ${GIT_VERSION} | cut -d- -f3)"
+      GIT_HASH="$(echo ${GIT_VERSION} | cut -d- -f4)"
+    else
+      LAST_TAG="$(echo ${GIT_VERSION} | cut -d- -f1)"
+      COMMITS_SINCE_TAG="$(echo ${GIT_VERSION} | cut -d- -f2)"
+      GIT_HASH="$(echo ${GIT_VERSION} | cut -d- -f3)"
+    fi
   fi
 
-  LAST_TAG="$(echo ${GIT_VERSION} | cut -d- -f1)"
-  COMMITS_SINCE_TAG="$(echo ${GIT_VERSION} | cut -d- -f2)"
-  GIT_HASH="$(echo ${GIT_VERSION} | cut -d- -f3)"
-
   if [ -z "${GIT_VERSION}" ]; then
+    # BUILDER_SUPPORT has more than one product listed, fall back to the 0.0.0 logic
+
     # We used 0.0.XXXXgHASH for master in the previous incarnation of our build pipeline.
     # This now becomes 0.0.XXXX.0.gHASH, as 0.0.0.XXXX.gHASH (which is more correct)
     # would break upgrades for those running master
@@ -45,11 +54,10 @@ if [ ! -z "$(git rev-parse --abbrev-ref HEAD 2> /dev/null)" ]; then
     LAST_TAG=0.0
     COMMITS_SINCE_TAG="$(git rev-list --count 12c868770afc20b6cc0da439d881105151d557dd..HEAD 2> /dev/null).0"
     [ "${COMMITS_SINCE_TAG}" = ".0" ] && COMMITS_SINCE_TAG=0.0
-    GIT_HASH="$(git rev-parse HEAD | cut -c1-10 2> /dev/null)"
+    GIT_HASH="g$(git rev-parse HEAD | cut -c1-10 2> /dev/null)"
   fi
 
   BRANCH=".$(git rev-parse --abbrev-ref HEAD | perl -p -e 's/[^[:alnum:]]//g;')"
-  [ "${BRANCH}" = ".master" ] && BRANCH=''
 
   TAG="$(git describe --tags --exact-match 2> /dev/null | cut -d- -f 2-)"
   if [ -n "${TAG}" ]; then # We're exactly on a tag
@@ -60,7 +68,7 @@ if [ ! -z "$(git rev-parse --abbrev-ref HEAD 2> /dev/null)" ]; then
     fi
   fi
 
-  VERSION="${LAST_TAG}.${COMMITS_SINCE_TAG}${BRANCH}.g${GIT_HASH}${DIRTY}"
+  VERSION="${LAST_TAG}.${COMMITS_SINCE_TAG}${BRANCH}.${GIT_HASH}${DIRTY}"
 fi
 
 printf $VERSION
index 1c00fff9756336949b49656bffb9098010cf281d..501661e6a7faedfb93cd35b1709c51f6e813a80f 100644 (file)
@@ -225,15 +225,7 @@ done
 
 PDNS_ENABLE_REMOTEBACKEND_ZEROMQ
 
-AC_MSG_CHECKING([whether we will be building and installing the extra tools])
-AC_ARG_ENABLE([tools],
-  [AS_HELP_STRING([--enable-tools], [if we should build and install the tools @<:@default=no@:>@])],
-  [enable_tools=$enableval],
-  [enable_tools=no]
-)
-
-AC_MSG_RESULT([$enable_tools])
-AM_CONDITIONAL([TOOLS], [test "x$enable_tools" != "xno"])
+PDNS_ENABLE_TOOLS
 PDNS_ENABLE_IXFRDIST
 
 PDNS_WITH_PROTOBUF
index 5434f3ec58fd8aa9f8e95eaf252f292ab4de225b..df627bd04b9035bb251e124c3797b0627b4f7ec3 100644 (file)
@@ -80,6 +80,13 @@ during the zonetransfer.
    it will (silently) serve it without DNSSEC. This in turn results in
    serving the domain as bogus.
 
+.. _setting-bind-dnssec-db-journal-mode:
+
+``bind-dnssec-db-journal-mode``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+SQLite3 journal mode to set. The default is WAL. Set to empty to leave the journal mode alone.
+
 .. _setting-bind-hybrid:
 
 ``bind-hybrid``
index 5cbf04418f4eb44fb79b4ea50a621e486b853f62..7dd9a7478a4c9d83a0adf70fa82aa04da26474bc 100644 (file)
@@ -21,6 +21,14 @@ Generic MySQL backend
   point this is going to harm your database, for example when an incoming
   zone transfer fails.
 
+.. warning::
+  While it is possible to run the Generic MySQL backend on top of MySQL
+  views, we have received several reports of this causing performance
+  problems and memory leaks.  Please know that when reporting problems when
+  running PowerDNS on top of a modified schema, our open source support
+  offering requires you to reproduce your problem on an unmodified schema without
+  views.
+
 The default schema is included at the bottom of this page.
 :ref:`migration-zone2sql` with the ``--gmysql`` flag also
 assumes this layout is in place. For full migration notes, please see
@@ -127,7 +135,7 @@ server. A value of 0 will disable the timeout. Default: 10
 .. _setting-gmysql-thread-cleanup:
 
 ``gmysql-thread-cleanup``
-^^^^^^^^^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^
 .. versionadded:: 4.1.8
 
 Older versions (such as those shipped on RHEL 7) of the MySQL/MariaDB client libraries leak memory unless applications explicitly report the end of each thread to the library. Enabling ``gmysql-thread-cleanup`` tells PowerDNS to call ``mysql_thread_end()`` whenever a thread ends.
index b0e723aceea839688aed483f15a8f70a868d132c..e2c595720321e2efe28a4b01a7a0d96fb80357d9 100644 (file)
@@ -68,6 +68,13 @@ gsqlite3 backend.
 
 Path to the SQLite3 database.
 
+.. _setting-gsqlite3-pragma-journal-mode:
+
+``gsqlite3-pragma-journal-mode``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+SQLite3 journal mode to set. The default is WAL. Set to empty to leave the journal mode alone.
+
 .. _setting-gsqlite3-pragma-synchronous:
 
 ``gsqlite3-pragma-synchronous``
index b86d45643a3428ad6acbf0f2dff7e205d1264434..4b3811d67ef9383cd809d1f13d4d728ddd8e079e 100644 (file)
@@ -94,7 +94,7 @@ caching options described
 ``geoip-zones-file``
 ~~~~~~~~~~~~~~~~~~~~
 
-Specifies the full path of the zone configuration file to use. The file is re-opened with a ``pdns_control reload''.
+Specifies the full path of the zone configuration file to use. The file is re-opened with a ``pdns_control reload``.
 
 .. _setting-geoip-dnssec-keydir:
 
index b71c933cd1ea3250aeacacf9057fa80df42d8f26..084378bdba9b2ec4207233fcdc1d0769cba765ea 100644 (file)
@@ -22,6 +22,8 @@ The following table describes the supported backends and some of their capabilit
 +------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+
 | :doc:`LDAP <ldap>`                             | Yes    | No     | No    | No           | No          | No                              | ``ldap``     |
 +------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+
+| :doc:`LMDB <lmdb>`                             | Yes    | Yes    | Yes   | No           | Yes         | Yes                             | ``lmdb``     |
++------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+
 | :doc:`Lua <lua>`                               | Yes    | Yes    | No    | No           | Yes         | Yes                             | ``lua``      |
 +------------------------------------------------+--------+--------+-------+--------------+-------------+---------------------------------+--------------+
 | :doc:`Lua2 <lua2>`                             | Yes    | Yes    | No    | No           | Yes         | Yes                             | ``lua2``     |
@@ -57,6 +59,7 @@ These backends have :doc:`features unique <generic-sql>` to the generic SQL back
   generic-sqlite3
   geoip
   ldap
+  lmdb
   lua
   lua2
   mydns
diff --git a/docs/backends/lmdb.rst b/docs/backends/lmdb.rst
new file mode 100644 (file)
index 0000000..419899c
--- /dev/null
@@ -0,0 +1,62 @@
+LMDB backend
+============
+
+* Native: Yes
+* Master: Yes
+* Slave: Yes
+* Superslave: No
+* Case: All lower
+* DNSSEC: Yes 
+* Disabled data: No
+* Comments: No
+* Module name: lmdb
+* Launch name: ``lmdb``
+
+Enabling the backend
+--------------------
+
+When building PowerDNS yourself, append ``lmdb`` to ``--with-modules`` or ``--with-dynmodules``. It is expected that most pre-built packages contain this backend or be separately installable.
+
+
+Settings
+--------
+
+.. _setting-lmdb-filename:
+
+``lmdb-filename``
+^^^^^^^^^^^^^^^^^
+
+Path to the LMDB file (e.g. */var/spool/powerdns/pdns.lmdb*)
+
+.. warning::
+  On systemd systems, 
+  When running PowerDNS via the provided systemd service file, `ProtectSystem <http://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=>`_ is set to ``full``, this means PowerDNS is unable to write to e.g. ``/etc`` and ``/home``, possibly being unable to write to the LMDB database.
+
+.. _setting-lmdb-shards:
+
+``lmdb-shards``
+^^^^^^^^^^^^^^^^^
+
+Records database will be split into this number of shards e.g. lmdb-shards=64
+Default is 2 on 32 bits systems, and 64 on 64 bits systems.
+
+.. _setting-lmdb-sync-mode:
+
+``lmdb-sync-mode``
+^^^^^^^^^^^^^^^^^^
+
+Synchronisation mode: sync, nosync, nometasync, mapasync
+Default: mapasync
+
+* ``sync``: LMDB synchronous mode. Safest option, but also slightly slower. Can  also be enabled with ``lmdb-sync-mode=`` 
+* ``nosync``: don't flush systems buffers to disk when committing a transation.
+  This means a system crash can corrupt the database or lose the last transactions if buffers are not yet flushed to disk.
+* ``nometasync``: flush system buffers to disk only once per transaction, omit the metadata flush. This maintains database integrity, but can potentially lose the last committed transaction if the operating system crashes.
+* ``mapasync``: (default). Use asynchronous flushes to disk. As with nosync, a system crash can then corrupt the database or lose the last transactions.
+
+
+LMDB Structure
+--------------
+
+PowerDNS will create the database structure, no need to manually create the database schema.
+Also, it is not possible to directly query the LMDB DB, so recommendation is to use either the API, or pdnsutil.
index 0bbda86c50c5fb0722d56760e730871367d54bab..8e26bbe6cb15bcb13c639be3bf8de9e746a2b715 100644 (file)
@@ -22,6 +22,7 @@ RREntry
   :property string content: The content of this record
   :property bool disabled: Whether or not this record is disabled
   :property bool set-ptr: If set to true, the server will find the matching reverse zone and create a PTR there. Existing PTR records are replaced. If no matching reverse :json:object:`Zone`, an error is thrown. Only valid in client bodies, only valid for A and AAAA types. Not returned by the server.
+  This feature (set-ptr) is deprecated and will be removed in 4.3.0.
 
 
 Comment
index 6e2b6997076acb216abeb6129e4af4f039c52ca1..4321d59c5f4aaacc64a644e0f5f3e0fc8fe96578 100644 (file)
@@ -26,7 +26,6 @@ for more information):
 -  RSASHA1 (algorithm 5, algorithm 7)
 -  RSASHA256 (algorithm 8)
 -  RSASHA512 (algorithm 10)
--  ECC-GOST (algorithm 12)
 -  ECDSA (algorithm 13 and 14)
 -  ed25519 (algorithm 15)
 -  ed448 (algorithm 16)
@@ -37,7 +36,6 @@ are supported:
 
 -  SHA-1 (algorithm 1)
 -  SHA-256 (algorithm 2)
--  GOST R 34.11-94 (algorithm 3)
 -  SHA-384 (algorithm 4)
 
 This corresponds to:
@@ -48,7 +46,6 @@ This corresponds to:
 - :rfc:`4509`: Use of SHA-256 in DNSSEC Delegation Signer (DS) Resource Records (RRs)
 - :rfc:`5155`: DNS Security (DNSSEC) Hashed Authenticated Denial of Existence
 - :rfc:`5702`: Use of SHA-2 Algorithms with RSA in DNSKEY and RRSIG Resource Records for DNSSEC
-- :rfc:`5933`: Use of GOST Signature Algorithms in DNSKEY and RRSIG Resource Records for DNSSEC
 - :rfc:`6605`: Elliptic Curve Digital Signature Algorithm (DSA) for DNSSEC
 - :rfc:`8080`: Edwards-Curve Digital Security Algorithm (EdDSA) for DNSSEC
 
index 63fcb3c4d65d01a232427a9e8bc7331d2d04bc0c..fa0fe9528bbd319fc024f9c033e762fd49700a79 100644 (file)
@@ -1024,7 +1024,7 @@ definitions:
         description: 'Whether or not this record is disabled'
       set-ptr:
         type: boolean
-        description: 'If set to true, the server will find the matching reverse zone and create a PTR there. Existing PTR records are replaced. If no matching reverse Zone, an error is thrown. Only valid in client bodies, only valid for A and AAAA types. Not returned by the server.'
+        description: 'If set to true, the server will find the matching reverse zone and create a PTR there. Existing PTR records are replaced. If no matching reverse Zone, an error is thrown. Only valid in client bodies, only valid for A and AAAA types. Not returned by the server. This feature is deprecated and will be removed in 4.3.0.'
 
   Comment:
     title: Comment
index fc19e119b80ffa1012f61e18400e0428322bec8d..8d144357b09301768f0b999f5c9b01e057642847 100644 (file)
@@ -4,28 +4,34 @@ sdig
 Synopsis
 --------
 
-:program:`sdig` *IPADDRESS* *PORT* *QNAME* *QTYPE* [*OPTION*]
+:program:`sdig` *IP-ADDRESS-OR-DOH-URL * *PORT* *QNAME* *QTYPE* [*OPTION*]
 
 Description
 -----------
 
-:program:`sdig` sends a DNS query to *IPADDRESS* on port *PORT* and displays
+:program:`sdig` sends a DNS query to *IP-ADDRESS-OR-DOH-URL* on port *PORT* and displays
 the answer in a formatted way.
+If the address starts with an ``h``, it is assumed to be a DoH endpoint, and *PORT* is ignored.
 
 Options
 -------
 
-These options can be added to the commandline in any order. dnssec : Set
-the DO bit to request DNSSEC information.
+These options can be added to the commandline in any order.
 
+dnssec
+    Set the DO bit to request DNSSEC information.
+ednssubnet *SUBNET*
+    Send *SUBNET* in the edns-client-subnet option. If this option is
+    not set, no edns-client-subnet option is set in the query.
 hidesoadetails
     Don't show the SOA serial in the response.
+hidettl
+    Replace TTLs with `[ttl]` in the response.
 recurse
     Set the RD bit in the question.
 showflags
-    Show the NSEC3 flags in the response.
+    Show the NSEC3 flags in the response (they are hidden by default).
 tcp
     Use TCP instead of UDP to send the query.
-ednssubnet *SUBNET*
-    Send *SUBNET* in the edns-client-subnet option. If this option is
-    not set, no edns-client-subnet option is set in the query.
+xpf *XPFCODE* *XPFVERSION* *XPFPROTO* *XPFSRC* *XPFSRC*
+       Send an *XPF* additional with these parameters.
\ No newline at end of file
index 9eafb750f4e6d196737e2438d7274aff6a34e98c..0e0d8e3e94eb39e68237990604247b00b9570280 100644 (file)
@@ -189,7 +189,7 @@ Supermaster: automatic provisioning of slaves
 
 .. versionchanged:: 4.2.0
   Supermaster support needs to be explicitly enabled with the
-  :ref:`setting-supermaster` setting.
+  :ref:`setting-superslave` setting.
 
 PowerDNS can recognize so called 'supermasters'. A supermaster is a host
 which is master for domains and for which we are to be a slave. When a
index 14012a04532c92fbea56dd4df98c172ef32835d4..ec04ba4f92ae50e5aae4214507f314610ec958db 100644 (file)
@@ -1,4 +1,4 @@
-@       86400   IN  SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2019050701 10800 3600 604800 10800
+@       86400   IN  SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2019060601 10800 3600 604800 10800
 @       3600    IN  NS  pdns-public-ns1.powerdns.com.
 @       3600    IN  NS  pdns-public-ns2.powerdns.com.
 ; Auth
@@ -181,8 +181,10 @@ recursor-4.1.9.security-status                          60 IN TXT "1 OK"
 recursor-4.1.10.security-status                         60 IN TXT "1 OK"
 recursor-4.1.11.security-status                         60 IN TXT "1 OK"
 recursor-4.1.12.security-status                         60 IN TXT "1 OK"
+recursor-4.1.13.security-status                         60 IN TXT "1 OK"
 recursor-4.2.0-alpha1.security-status                   60 IN TXT "1 OK"
 recursor-4.2.0-beta1.security-status                    60 IN TXT "1 OK"
+recursor-4.2.0-rc1.security-status                      60 IN TXT "1 OK"
 
 ; Recursor Debian
 recursor-3.6.2-2.debian.security-status                 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/"
@@ -303,3 +305,4 @@ recursor-4.0.0_beta1-1pdns.jessie.raspbian.security-status 60 IN TXT "3 Upgrade
 dnsdist-1.3.3.security-status                              60 IN TXT "1 OK"
 dnsdist-1.4.0-alpha1.security-status                       60 IN TXT "1 OK"
 dnsdist-1.4.0-alpha2.security-status                       60 IN TXT "1 OK"
+dnsdist-1.4.0-beta1.security-status                        60 IN TXT "1 OK"
index 5d35c0ddea625c82c687f7bd819291b43b2acd55..50fa520f03511a54d9ae1aaa6932dbccea86382f 100644 (file)
@@ -508,6 +508,9 @@ inside a supervisor that handles logging (like systemd).
 -  Boolean
 -  Default: no
 
+.. versionchanged:: 4.2.0
+  This setting has been removed
+
 Do not listen to TCP queries. Breaks RFC compliance.
 
 .. _setting-distributor-threads:
@@ -1556,6 +1559,8 @@ IP address of incoming notification proxy
 
 ``udp-truncation-threshold``
 ----------------------------
+.. versionchanged:: 4.2.0
+  Before 4.2.0, the default was 1680
 
 -  Integer
 -  Default: 1232
index b0b7ba64ff2e77b39c756fd8cf53e2c89caaad5f..622d1087d14358e7738bad550550a8b7386c8c5e 100644 (file)
@@ -12,6 +12,7 @@ upgrade notes if your version is older than 3.4.2.
 --------------
 
 - Superslave operation is no longer enabled by default, use :ref:`setting-superslave` to enable. This setting was called ``supermaster`` in some 4.2.0 prereleases.
+- The gsqlite3 backend, and the DNSSEC database for the BIND backend, have a new journal-mode setting. This setting defaults to `WAL <https://www.sqlite.org/wal.html>`_; older versions of PowerDNS did not set the journal mode, which means they used the SQLite default of DELETE.
 
 4.1.0 to 4.1.1
 --------------
index 96b4b54f60b3ed4d0fede309bc5c90ff62f6025e..3baede43af4bbd2f7a88dc452381dfc9d40650bd 100644 (file)
@@ -22,7 +22,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 m4_define([_BOOST_SERIAL], [m4_translit([
-# serial 27
+# serial 28
 ], [#
 ], [])])
 
@@ -644,6 +644,7 @@ LDFLAGS=$boost_filesystem_save_LDFLAGS
 # * This library was introduced in Boost 1.51.0
 # * The signatures of make_fcontext() and jump_fcontext were changed in 1.56.0
 # * A dependency on boost_thread appears in 1.57.0
+# * The implementation details were moved to boost::context::detail in 1.61.0
 BOOST_DEFUN([Context],
 [boost_context_save_LIBS=$LIBS
  boost_context_save_LDFLAGS=$LDFLAGS
@@ -653,6 +654,32 @@ if test $boost_major_version -ge 157; then
   LIBS="$LIBS $BOOST_THREAD_LIBS"
   LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS"
 fi
+
+if test $boost_major_version -ge 161; then
+BOOST_FIND_LIB([context], [$1],
+                [boost/context/continuation.hpp], [[
+namespace ctx=boost::context;
+int a;
+ctx::continuation source=ctx::callcc(
+    [&a](ctx::continuation && sink){
+        a=0;
+        int b=1;
+        for(;;){
+            sink=sink.resume();
+            int next=a+b;
+            a=b;
+            b=next;
+        }
+        return std::move(sink);
+    });
+for (int j=0;j<10;++j) {
+    source=source.resume();
+}
+return a == 34;
+]])
+
+else
+
 BOOST_FIND_LIB([context], [$1],
                 [boost/context/fcontext.hpp],[[
 
@@ -710,6 +737,9 @@ static void f(intptr_t i) {
 }
 #endif
 ])
+
+fi
+
 LIBS=$boost_context_save_LIBS
 LDFLAGS=$boost_context_save_LDFLAGS
 ])# BOOST_CONTEXT
@@ -1516,6 +1546,8 @@ if test x$boost_cv_inc_path != xno; then
   # I'm not sure about my test for `il' (be careful: Intel's ICC pre-defines
   # the same defines as GCC's).
   for i in \
+    _BOOST_mingw_test(8, 3) \
+    _BOOST_gcc_test(8, 3) \
     _BOOST_mingw_test(8, 2) \
     _BOOST_gcc_test(8, 2) \
     _BOOST_mingw_test(8, 1) \
diff --git a/m4/pdns_check_libcurl.m4 b/m4/pdns_check_libcurl.m4
new file mode 100644 (file)
index 0000000..43412de
--- /dev/null
@@ -0,0 +1,6 @@
+AC_DEFUN([PDNS_CHECK_LIBCURL], [
+  AS_IF([test -n "$HAVE_LIBCURL"], [ : ], [
+    LIBCURL_CHECK_CONFIG("yes", "7.21.3", [ HAVE_LIBCURL=y ], [ HAVE_LIBCURL=n ])
+  ])
+  AM_CONDITIONAL([HAVE_LIBCURL], [test "$HAVE_LIBCURL" = "y"])
+])
index 860f9aa0e35029fc897694041e36ab55f4decc57..da4bc8fd111b7b88ddd37ee76fadd8400e674304 100644 (file)
@@ -35,16 +35,21 @@ AC_DEFUN([PDNS_CHECK_OS],[
   AM_CONDITIONAL([HAVE_LINUX], [test "x$have_linux" = "xyes"])
   AM_CONDITIONAL([HAVE_SOLARIS], [test "x$have_solaris" = "xyes"])
 
-  case "$host" in
-  mips* | powerpc-* )
-    AC_MSG_CHECKING([whether the linker accepts -latomic])
-    LDFLAGS="-latomic $LDFLAGS"
-    AC_LINK_IFELSE([m4_default([],[AC_LANG_PROGRAM()])],
-      [AC_MSG_RESULT([yes])],
-      [AC_MSG_ERROR([Unable to link against libatomic, cannot continue])]
-    )
-    ;;
-  esac
+  AC_MSG_CHECKING([whether -latomic is needed for __atomic builtins])
+  AC_LINK_IFELSE(
+    [AC_LANG_PROGRAM([[#include <stdint.h>]],
+       [[uint64_t val = 0; __atomic_add_fetch(&val, 1, __ATOMIC_RELAXED);]]
+    )],
+    [AC_MSG_RESULT([no])],
+    [LIBS="$LIBS -latomic"
+     AC_LINK_IFELSE(
+       [AC_LANG_PROGRAM([[#include <stdint.h>]],
+               [[uint64_t val = 0; __atomic_add_fetch(&val, 1, __ATOMIC_RELAXED);]]
+       )],
+       [AC_MSG_RESULT([yes])],
+       [AC_MSG_FAILURE([libatomic needed, but linking with -latomic failed, cannot continue])]
+    )]
+  )
 
   AC_SUBST(THREADFLAGS)
   AC_SUBST([DYNLINKFLAGS], [-export-dynamic])
diff --git a/m4/pdns_enable_tools.m4 b/m4/pdns_enable_tools.m4
new file mode 100644 (file)
index 0000000..4324f8e
--- /dev/null
@@ -0,0 +1,12 @@
+AC_DEFUN([PDNS_ENABLE_TOOLS], [
+  AC_REQUIRE([PDNS_CHECK_LIBCURL]) dnl We only care about the #define HAVE_LIBCURL and can build tools without DOH support.
+  AC_MSG_CHECKING([whether we will be building and installing the extra tools])
+  AC_ARG_ENABLE([tools],
+    [AS_HELP_STRING([--enable-tools], [if we should build and install the tools @<:@default=no@:>@])],
+    [enable_tools=$enableval],
+    [enable_tools=no]
+    )
+  AC_MSG_RESULT([$enable_tools])
+
+  AM_CONDITIONAL([TOOLS], [test "x$enable_tools" != "xno"])
+])
index a3f2419c87b1e7b255388ba5e43769c5c346017f..763ee8fa6967f284a58667ba945cfa67e9699f18 100644 (file)
@@ -1,4 +1,5 @@
 AC_DEFUN([PDNS_WITH_LUA_RECORDS], [
+  AC_REQUIRE([PDNS_CHECK_LIBCURL])
   AC_MSG_CHECKING([whether we will enable LUA records])
 
   AC_ARG_ENABLE([lua-records],
@@ -12,9 +13,10 @@ AC_DEFUN([PDNS_WITH_LUA_RECORDS], [
     AS_IF([test "x$LUAPC" = "x"],
       AC_MSG_ERROR([LUA records need LUA. You can disable this feature with the --disable-lua-records switch or configure a proper LUA installation.])
     )
-    LIBCURL_CHECK_CONFIG("yes", "7.21.3", [ : ], [
+    AS_IF([test "$HAVE_LIBCURL" != "y"], [
       AC_MSG_ERROR([libcurl minimum version requirement not met. This is required for LUA records. You can disable it with the --disable-lua-records switch or use --with-libcurl to select another curl installation.])
     ])
+
     AC_DEFINE([HAVE_LUA_RECORDS], [1], [Define if enabling LUA records.])
   ])
   AM_CONDITIONAL([HAVE_LUA_RECORDS], [test "x$enable_lua_records" != "xno"])
index 8040672e6cb5850709b5fb1c4d0baa4a8c354ad1..d4cfcfac6881ce1bdb06fa48253981c0307fe9d3 100644 (file)
@@ -10,8 +10,11 @@ AC_DEFUN([PDNS_WITH_NET_SNMP], [
   AS_IF([test "x$with_net_snmp" != "xno"], [
     AS_IF([test "x$with_net_snmp" = "xyes" -o "x$with_net_snmp" = "xauto"], [
       AC_CHECK_PROG([NET_SNMP_CFLAGS], [net-snmp-config], [`net-snmp-config --cflags`])
-      AC_CHECK_PROG([NET_SNMP_LIBS], [net-snmp-config], [`net-snmp-config --agent-libs`])
-      AC_CHECK_DECLS([snmp_select_info2], [ : ], [ : ],
+      AC_CHECK_PROG([NET_SNMP_LIBS], [net-snmp-config], [`net-snmp-config --netsnmp-agent-libs`])
+      AC_CHECK_DECLS([snmp_select_info2], [
+          AC_DEFINE([HAVE_SNMP_SELECT_INFO2], [1], [define to 1 if snmp_select_info2 is available.])
+        ],
+        [ : ],
         [AC_INCLUDES_DEFAULT
           #include <net-snmp/net-snmp-config.h>
           #include <net-snmp/definitions.h>
index 69d0450fc9999bbb8e2ecdc36a77ce9f85e97482..cc89bff385971a51ca523f877d2bfc126d2408b6 100644 (file)
@@ -203,17 +203,27 @@ bool Bind2Backend::startTransaction(const DNSName &qname, int id)
   d_transaction_id=id;
   BB2DomainInfo bbd;
   if(safeGetBBDomainInfo(id, &bbd)) {
-    d_transaction_tmpname=bbd.d_filename+"."+itoa(random());
-    d_of=std::unique_ptr<ofstream>(new ofstream(d_transaction_tmpname.c_str()));
+    d_transaction_tmpname = bbd.d_filename + "XXXXXX";
+    int fd = mkstemp(&d_transaction_tmpname.at(0));
+    if (fd == -1) {
+      throw DBException("Unable to create a unique temporary zonefile '"+d_transaction_tmpname+"': "+stringerror());
+      return false;
+    }
+
+    d_of = std::unique_ptr<ofstream>(new ofstream(d_transaction_tmpname.c_str()));
     if(!*d_of) {
       unlink(d_transaction_tmpname.c_str());
+      close(fd);
+      fd = -1;
       d_of.reset();
       throw DBException("Unable to open temporary zonefile '"+d_transaction_tmpname+"': "+stringerror());
     }
-    
+    close(fd);
+    fd = -1;
+
     *d_of<<"; Written by PowerDNS, don't edit!"<<endl;
     *d_of<<"; Zone '"<<bbd.d_name<<"' retrieved from master "<<endl<<"; at "<<nowTime()<<endl; // insert master info here again
-    
+
     return true;
   }
   return false;
@@ -603,6 +613,7 @@ string Bind2Backend::DLAddDomainHandler(const vector<string>&parts, Utility::pid
   bbd.d_loaded=true;
   bbd.d_lastcheck=0;
   bbd.d_status="parsing into memory";
+  bbd.setCtime();
 
   safePutBBDomainInfo(bbd);
 
@@ -630,6 +641,7 @@ Bind2Backend::Bind2Backend(const string &suffix, bool loadZones)
   setArgPrefix("bind"+suffix);
   d_logprefix="[bind"+suffix+"backend]";
   d_hybrid=mustDo("hybrid");
+  d_transaction_id=0;
   s_ignore_broken_records=mustDo("ignore-broken-records");
 
   if (!loadZones && d_hybrid)
@@ -637,7 +649,6 @@ Bind2Backend::Bind2Backend(const string &suffix, bool loadZones)
 
   Lock l(&s_startup_lock);
   
-  d_transaction_id=0;
   setupDNSSEC();
   if(!s_first) {
     return;
@@ -1262,11 +1273,13 @@ BB2DomainInfo Bind2Backend::createDomainEntry(const DNSName& domain, const strin
   }
   
   BB2DomainInfo bbd;
+  bbd.d_kind = DomainInfo::Native;
   bbd.d_id = newid;
   bbd.d_records = shared_ptr<recordstorage_t >(new recordstorage_t);
   bbd.d_name = domain;
   bbd.setCheckInterval(getArgAsNum("check-interval"));
   bbd.d_filename = filename;
+  
   return bbd;
 }
 
@@ -1300,6 +1313,7 @@ bool Bind2Backend::createSlaveDomain(const string &ip, const DNSName& domain, co
   BB2DomainInfo bbd = createDomainEntry(domain, filename);
   bbd.d_kind = DomainInfo::Slave;
   bbd.d_masters.push_back(ComboAddress(ip, 53));
+  bbd.setCtime();
   safePutBBDomainInfo(bbd);
   return true;
 }
@@ -1316,7 +1330,10 @@ bool Bind2Backend::searchRecords(const string &pattern, int maxResults, vector<D
 
     for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
       BB2DomainInfo h;
-      safeGetBBDomainInfo(i->d_id, &h);
+      if (!safeGetBBDomainInfo(i->d_id, &h)) {
+        continue;
+      }
+
       shared_ptr<const recordstorage_t> rhandle = h.d_records.get();
 
       for(recordstorage_t::const_iterator ri = rhandle->begin(); result.size() < static_cast<vector<DNSResourceRecord>::size_type>(maxResults) && ri != rhandle->end(); ri++) {
@@ -1352,6 +1369,7 @@ class Bind2Factory : public BackendFactory
          declare(suffix,"supermasters","List of IP-addresses of supermasters","");
          declare(suffix,"supermaster-destdir","Destination directory for newly added slave zones",::arg()["config-dir"]);
          declare(suffix,"dnssec-db","Filename to store & access our DNSSEC metadatabase, empty for none", "");         
+         declare(suffix,"dnssec-db-journal-mode","SQLite3 journal mode", "WAL");
          declare(suffix,"hybrid","Store DNSSEC metadata in other backend","no");
       }
 
index 424c5683b0865b39599b4624b5b474c31d6c3e2c..ee545d636555e69d42c4fdb5340e5cd6232a590c 100644 (file)
@@ -165,9 +165,9 @@ public:
   vector<ComboAddress> d_masters;     //!< IP address of the master of this domain
   set<string> d_also_notify; //!< IP list of hosts to also notify
   LookButDontTouch<recordstorage_t> d_records;  //!< the actual records belonging to this domain
-  time_t d_ctime;  //!< last known ctime of the file on disk
-  time_t d_lastcheck; //!< last time domain was checked for freshness
-  uint32_t d_lastnotified; //!< Last serial number we notified our slaves of
+  time_t d_ctime{0};  //!< last known ctime of the file on disk
+  time_t d_lastcheck{0}; //!< last time domain was checked for freshness
+  uint32_t d_lastnotified{0}; //!< Last serial number we notified our slaves of
   unsigned int d_id;  //!< internal id of the domain
   mutable bool d_checknow; //!< if this domain has been flagged for a check
   bool d_loaded;  //!< if a domain is loaded
index f254ec5c7a445556db20dbc641039458e50bc08b..93996982e96534df5fa5f35ffb9960ec3d77afe5 100644 (file)
@@ -96,7 +96,7 @@ void Bind2Backend::setupDNSSEC()
   if(getArg("dnssec-db").empty() || d_hybrid)
     return;
   try {
-    d_dnssecdb = shared_ptr<SSQLite3>(new SSQLite3(getArg("dnssec-db")));
+    d_dnssecdb = shared_ptr<SSQLite3>(new SSQLite3(getArg("dnssec-db"), getArg("dnssec-db-journal-mode")));
     setupStatements();
   }
   catch(SSqlException& se) {
index 48707d3a95f250ea6780d2c21484fb2a05a02968..48b8c4c1bb7c05bda83289d634d4d9ee4cdc2ef6 100644 (file)
@@ -122,7 +122,7 @@ public:
     declare(suffix,"update-account-query","", "update domains set account=? where name=?");
     declare(suffix,"update-serial-query","", "update domains set notified_serial=? where id=?");
     declare(suffix,"update-lastcheck-query","", "update domains set last_check=? where id=?");
-    declare(suffix,"info-all-master-query","", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
+    declare(suffix,"info-all-master-query","", "select d.id, d.name, d.notified_serial, r.content from records r join domains d on r.name=d.name where r.type='SOA' and r.disabled=0 and d.type='MASTER'");
     declare(suffix,"delete-domain-query","", "delete from domains where name=?");
     declare(suffix,"delete-zone-query","", "delete from records where domain_id=?");
     declare(suffix,"delete-rrset-query","","delete from records where domain_id=? and name=? and type=?");
index 7334df4c9d5e0c71a7d7de21ad19978ea1e4965e..533f50f959b39c0bce226bd0430a208d647a4e79 100644 (file)
@@ -107,7 +107,7 @@ public:
     declare(suffix,"update-account-query","", "update domains set account=? where name=?");
     declare(suffix,"update-serial-query","", "update domains set notified_serial=? where id=?");
     declare(suffix,"update-lastcheck-query","", "update domains set last_check=? where id=?");
-    declare(suffix,"info-all-master-query","", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
+    declare(suffix,"info-all-master-query","", "select domains.id, domains.name, domains.notified_serial, records.content from records join domains on records.name=domains.name where records.type='SOA' and records.disabled=0 and domains.type='MASTER'");
     declare(suffix,"delete-domain-query","", "delete from domains where name=?");
     declare(suffix,"delete-zone-query","", "delete from records where domain_id=?");
     declare(suffix,"delete-rrset-query","","delete from records where domain_id=? and name=? and type=?");
index 641f74bb1b6498add1c3e650ae8f893df89afa95..cc1c601de87dc619328f94d236f852d77083588a 100644 (file)
@@ -129,7 +129,7 @@ public:
     declare(suffix,"update-account-query","", "update domains set account=$1 where name=$2");
     declare(suffix,"update-serial-query","", "update domains set notified_serial=$1 where id=$2");
     declare(suffix,"update-lastcheck-query","", "update domains set last_check=$1 where id=$2");
-    declare(suffix,"info-all-master-query","", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
+    declare(suffix,"info-all-master-query","", "select domains.id, domains.name, domains.notified_serial, records.content from records join domains on records.name=domains.name where records.type='SOA' and records.disabled=false and domains.type='MASTER'");
     declare(suffix,"delete-domain-query","", "delete from domains where name=$1");
     declare(suffix,"delete-zone-query","", "delete from records where domain_id=$1");
     declare(suffix,"delete-rrset-query","","delete from records where domain_id=$1 and name=$2 and type=$3");
index f9cb892fcbf911ed48c5de947edbef65b755a8cf..a188fc655e32cd54bc61ef50f74bb6be1ff5cc67 100644 (file)
@@ -43,7 +43,7 @@ gSQLite3Backend::gSQLite3Backend( const std::string & mode, const std::string &
 {
   try
   {
-    SSQLite3 *ptr = new SSQLite3( getArg( "database" ));
+    SSQLite3 *ptr = new SSQLite3( getArg( "database" ), getArg( "pragma-journal-mode") );
     setDB(ptr);
     if(!getArg("pragma-synchronous").empty()) {
       ptr->execute("PRAGMA synchronous="+getArg("pragma-synchronous"));
@@ -77,6 +77,7 @@ public:
     declare(suffix, "database", "Filename of the SQLite3 database", "powerdns.sqlite");
     declare(suffix, "pragma-synchronous", "Set this to 0 for blazing speed", "");
     declare(suffix, "pragma-foreign-keys", "Enable foreign key constraints", "no" );
+    declare(suffix, "pragma-journal-mode", "SQLite3 journal mode", "WAL");
 
     declare(suffix, "dnssec", "Enable DNSSEC processing","no");
 
@@ -119,7 +120,7 @@ public:
     declare(suffix, "update-account-query","", "update domains set account=:account where name=:domain");
     declare(suffix, "update-serial-query", "", "update domains set notified_serial=:serial where id=:domain_id");
     declare(suffix, "update-lastcheck-query", "", "update domains set last_check=:last_check where id=:domain_id");
-    declare(suffix, "info-all-master-query", "", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
+    declare(suffix, "info-all-master-query", "", "select domains.id, domains.name, domains.notified_serial, records.content from records join domains on records.name=domains.name where records.type='SOA' and records.disabled=0 and domains.type='MASTER'");
     declare(suffix, "delete-domain-query","", "delete from domains where name=:domain");
     declare(suffix, "delete-zone-query", "", "delete from records where domain_id=:domain_id");
     declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=:domain_id and name=:qname and type=:qtype");
index f69d78dd464bdc032753b9213ff2ecf2d8500c0c..c0f913fa367b5e5dce5812dcfd03a8c7e72d4da2 100644 (file)
@@ -372,7 +372,9 @@ bool LdapBackend::getDomainInfo( const DNSName& domain, DomainInfo& di, bool get
     // search for SOARecord of domain
     filter = "(&(associatedDomain=" + toLower( d_pldap->escape( domain.toStringRootDot() ) ) + ")(SOARecord=*))";
     d_search = d_pldap->search( getArg( "basedn" ), LDAP_SCOPE_SUBTREE, filter, attronly );
-    d_search->getNext( result );
+    if (!d_search->getNext( result )) {
+      return false;
+    }
   }
   catch( LDAPTimeout &lt )
   {
index de5e9aef850312848d10656c58432caff2a2d4b0..9b658741b4e11702112e16b902fbb0c8a5c9f0f5 100644 (file)
@@ -372,7 +372,7 @@ public:
     return d_cursor;
   }
 
-  MDB_cursor* d_cursor;
+  MDB_cursor* d_cursor{nullptr};
   Transaction* d_parent;
 };
 
@@ -389,13 +389,15 @@ public:
   MDBROCursor(MDBROCursor&& rhs) : MDBGenCursor<MDBROTransaction>(rhs.d_parent)
   {
     d_cursor = rhs.d_cursor;
-    rhs.d_cursor=0;
+    rhs.d_cursor = nullptr;
   }
 
   void close()
   {
-    mdb_cursor_close(d_cursor);
-    d_cursor=0;
+    if (d_cursor) {
+      mdb_cursor_close(d_cursor);
+      d_cursor = nullptr;
+    }
   }
   
   ~MDBROCursor()
@@ -568,7 +570,7 @@ public:
   MDBRWCursor(MDBRWCursor&& rhs) : MDBGenCursor<MDBRWTransaction>(rhs.d_parent)
   {
     d_cursor = rhs.d_cursor;
-    rhs.d_cursor=0;
+    rhs.d_cursor = nullptr;
     d_parent->reportCursorMove(&rhs, this);
   }
 
@@ -576,7 +578,7 @@ public:
   {
     if(d_cursor)
       mdb_cursor_close(d_cursor);
-    d_cursor=0;
+    d_cursor = nullptr;
   }
   
   ~MDBRWCursor()
index db40143f55ccd334a0e68f7aa2552e60c7c78d20..e78744a937b0664422da0f29177005c8b497cf9c 100644 (file)
@@ -397,8 +397,10 @@ bool LMDBBackend::replaceRRSet(uint32_t domain_id, const DNSName& qname, const Q
   }
 
   DomainInfo di;
-  d_tdomains->getROTransaction().get(domain_id, di); // XX error checking
-  
+  if (!d_tdomains->getROTransaction().get(domain_id, di)) {
+    return false;
+  }
+
   compoundOrdername co;
   auto cursor = txn->txn.getCursor(txn->db->dbi);
   MDBOutVal key, val;
@@ -616,8 +618,9 @@ bool LMDBBackend::get(DNSResourceRecord& rr)
   rr.qname = dzr.dr.d_name;
   rr.ttl = dzr.dr.d_ttl;
   rr.qtype =dzr.dr.d_type;
-  rr.content = dzr.dr.d_content->getZoneRepresentation();
+  rr.content = dzr.dr.d_content->getZoneRepresentation(true);
   rr.domain_id = dzr.domain_id;
+  rr.auth = dzr.auth;
   //  cout<<"old school called for "<<rr.qname<<", "<<rr.qtype.getName()<<endl;
   return true;
 }
index b479d4f4db8db4121ec7852d5c69ef1aa0313699..edf380e967554e8ce17a4f09c6742f5808e857e2 100644 (file)
@@ -503,6 +503,11 @@ sdig_SOURCES = \
 sdig_LDADD = $(LIBCRYPTO_LIBS)
 sdig_LDFLAGS = $(AM_LDFLAGS) $(LIBCRYPTO_LDFLAGS)
 
+if HAVE_LIBCURL
+sdig_SOURCES += minicurl.cc minicurl.hh
+sdig_LDADD += $(LIBCURL)
+endif
+
 calidns_SOURCES = \
        base32.cc \
        base64.cc base64.hh \
index c0104477dd3a3eca9c894c9caa0bc2f4a1534b65..7dc46b588bc5c2841bf7a5e4afe32f528810ea80 100644 (file)
@@ -417,7 +417,7 @@ void GSQLBackend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
 void GSQLBackend::getUpdatedMasters(vector<DomainInfo> *updatedDomains)
 {
   /* list all domains that need notifications for which we are master, and insert into updatedDomains
-     id,name,master IP,serial */
+     id, name, notified_serial, serial */
   try {
     reconnectIfNeeded();
 
@@ -430,33 +430,34 @@ void GSQLBackend::getUpdatedMasters(vector<DomainInfo> *updatedDomains)
     throw PDNSException("GSQLBackend unable to retrieve list of master domains: "+e.txtReason());
   }
 
-  vector<DomainInfo> allMasters;
   size_t numanswers=d_result.size();
-  for(size_t n=0;n<numanswers;++n) { // id,name,master,last_check,notified_serial
-    DomainInfo sd;
-    ASSERT_ROW_COLUMNS("info-all-master-query", d_result[n], 6);
-    sd.id=pdns_stou(d_result[n][0]);
+  vector<string>parts;
+  DomainInfo di;
+
+  di.backend = this;
+  di.kind = DomainInfo::Master;
+
+  for( size_t n = 0; n < numanswers; ++n ) { // id, name, notified_serial, content
+    ASSERT_ROW_COLUMNS( "info-all-master-query", d_result[n], 4 );
+
+    parts.clear();
+    stringtok( parts, d_result[n][3] );
+
     try {
-      sd.zone= DNSName(d_result[n][1]);
-    } catch (...) {
+      uint32_t serial = parts.size() > 2 ? pdns_stou(parts[2]) : 0;
+      uint32_t notified_serial = pdns_stou( d_result[n][2] );
+
+      if( serial != notified_serial ) {
+        di.id = pdns_stou( d_result[n][0] );
+        di.zone = DNSName( d_result[n][1] );
+        di.serial = serial;
+        di.notified_serial = notified_serial;
+
+        updatedDomains->emplace_back(di);
+      }
+    } catch ( ... ) {
       continue;
     }
-    sd.last_check=pdns_stou(d_result[n][3]);
-    sd.notified_serial=pdns_stou(d_result[n][4]);
-    sd.backend=this;
-    sd.kind=DomainInfo::Master;
-    allMasters.push_back(sd);
-  }
-
-  for(vector<DomainInfo>::iterator i=allMasters.begin();i!=allMasters.end();++i) {
-    SOAData sdata;
-    sdata.serial=0;
-    sdata.refresh=0;
-    getSOA(i->zone,sdata);
-    if(i->notified_serial!=sdata.serial) {
-      i->serial=sdata.serial;
-      updatedDomains->push_back(*i);
-    }
   }
 }
 
@@ -1324,8 +1325,12 @@ void GSQLBackend::getAllDomains(vector<DomainInfo> *domains, bool include_disabl
       SOAData sd;
       fillSOAData(row[2], sd);
       di.serial = sd.serial;
-      di.notified_serial = pdns_stou(row[5]);
-      di.last_check = pdns_stou(row[6]);
+      try {
+        di.notified_serial = pdns_stou(row[5]);
+        di.last_check = pdns_stou(row[6]);
+      } catch(...) {
+        continue;
+      }
       di.account = row[7];
 
       di.backend = this;
index bd57fc54235208771682b01da17d0c3a1d5e146d..07d78c1cd9f2ccb9eb2a55943891d55dd8de6a63 100644 (file)
@@ -52,7 +52,7 @@ DynListener *dl;
 CommunicatorClass Communicator;
 shared_ptr<UDPNameserver> N;
 int avg_latency;
-TCPNameserver *TN;
+unique_ptr<TCPNameserver> TN;
 static vector<DNSDistributor*> g_distributors;
 vector<std::shared_ptr<UDPNameserver> > g_udpReceivers;
 
@@ -117,7 +117,6 @@ void declareArguments()
   ::arg().set("queue-limit","Maximum number of milliseconds to queue a query")="1500"; 
   ::arg().set("resolver","Use this resolver for ALIAS and the internal stub resolver")="no";
   ::arg().set("udp-truncation-threshold", "Maximum UDP response size before we truncate")="1232";
-  ::arg().set("disable-tcp","Do not listen to TCP queries")="no";
   
   ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
 
@@ -511,6 +510,7 @@ void mainthread()
    g_8bitDNS = ::arg().mustDo("8bit-dns");
 #ifdef HAVE_LUA_RECORDS
    g_doLuaRecord = ::arg().mustDo("enable-lua-records");
+   g_LuaRecordSharedState = (::arg()["enable-lua-records"] == "shared");
    g_luaRecordExecLimit = ::arg().asNum("lua-records-exec-limit");
 #endif
 
@@ -607,8 +607,7 @@ void mainthread()
   if(::arg().mustDo("slave") || ::arg().mustDo("master") || !::arg()["forward-notify"].empty())
     Communicator.go(); 
 
-  if(TN)
-    TN->go(); // tcp nameserver launch
+  TN->go(); // tcp nameserver launch
 
   //  fork(); (this worked :-))
   unsigned int max_rthreads= ::arg().asNum("receiver-threads", 1);
index 1e1290ed57f30456169f8f473d8b808ea4834f1a..70502d4fbd5c72d88cbb5a4760f655dbb09de2e3 100644 (file)
@@ -46,7 +46,7 @@ extern CommunicatorClass Communicator;
 extern std::shared_ptr<UDPNameserver> N;
 extern vector<std::shared_ptr<UDPNameserver> > g_udpReceivers;
 extern int avg_latency;
-extern TCPNameserver *TN;
+extern std::unique_ptr<TCPNameserver> TN;
 extern ArgvMap & arg( void );
 extern void declareArguments();
 extern void declareStats();
@@ -57,6 +57,7 @@ extern bool g_anyToTcp;
 extern bool g_8bitDNS;
 #ifdef HAVE_LUA_RECORDS
 extern bool g_doLuaRecord;
+extern bool g_LuaRecordSharedState;
 #endif // HAVE_LUA_RECORDS
 
 #endif // COMMON_STARTUP_HH
index 709b8d05fa449850ee53409207f7a9064db17541..be8af617239e08a44601ff8c92370d11f06875a9 100644 (file)
@@ -139,7 +139,10 @@ void CommunicatorClass::mainloop(void)
           if (extraSlaveRefresh)
             slaveRefresh(&P);
         }
-        else { 
+        else {
+          // eat up extra posts to avoid busy looping if many posts were done
+          while (d_any_sem.tryWait() == 0) {
+          }
           break; // something happened
         }
         // this gets executed at least once every second
index bb6e8e615dd900664ecb844cbaf457369ec00869..48b910c8f4f85d7c2a561f664de3c52fc37dd922 100644 (file)
@@ -281,11 +281,25 @@ uint32_t dns_random(uint32_t upper_bound) {
 #endif
   case RNG_URANDOM: {
       uint32_t num = 0;
+      size_t attempts = 5;
       do {
-        if (read(urandom_fd, &num, sizeof(num)) < 0) {
+        ssize_t got = read(urandom_fd, &num, sizeof(num));
+        if (got < 0) {
+          if (errno == EINTR) {
+            continue;
+          }
+
           (void)close(urandom_fd);
           throw std::runtime_error("Cannot read random device");
         }
+        else if (static_cast<size_t>(got) != sizeof(num)) {
+          /* short read, let's retry */
+          if (attempts == 0) {
+            throw std::runtime_error("Too many short reads on random device");
+          }
+          attempts--;
+          continue;
+        }
       }
       while(num < min);
 
index 27a288173be8e29c02576c3c2b853d09eaef1d91..18bfeee6cf07776ae98909a899b570cc57cadd6b 100644 (file)
@@ -222,7 +222,7 @@ try
     ("type,t",  po::value<string>()->default_value("A"), "What type to query for")
     ("envoutput,e", "write report in shell environment format")
     ("version", "show the version number")
-    ("www", po::value<bool>()->default_value("true"), "duplicate all queries with an additional 'www.' in front")
+    ("www", po::value<bool>()->default_value(true), "duplicate all queries with an additional 'www.' in front")
   ;
 
   po::options_description alloptions;
@@ -293,7 +293,7 @@ try
     split=splitField(line,',');
     if (split.second.empty())
       split=splitField(line,'\t');
-    if(!split.second.find('.')) // skip 'Hidden profile' in quantcast list.
+    if(split.second.find('.') == 0) // skip 'Hidden profile' in quantcast list.
       continue;
     pos=split.second.find('/');
     if(pos != string::npos) // alexa has whole urls in the list now.
index 98bd43c9cb7f5bc829b97c01a03707db0b3ef072..65fcf8ab8814850ed85d1eb06a97a3d179905bdb 100644 (file)
@@ -343,10 +343,12 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   /* keyword, function, parameters, description */
   { "addACL", true, "netmask", "add to the ACL set who can use this server" },
   { "addAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "add a rule" },
+  { "addBPFFilterDynBlocks", true, "addresses, dynbpf[[, seconds=10], msg]", "This is the eBPF equivalent of addDynBlocks(), blocking a set of addresses for (optionally) a number of seconds, using an eBPF dynamic filter" },
   { "addConsoleACL", true, "netmask", "add a netmask to the console ACL" },
   { "addDNSCryptBind", true, "\"127.0.0.1:8443\", \"provider name\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", {reusePort=false, tcpFastOpenSize=0, interface=\"\", cpus={}}", "listen to incoming DNSCrypt queries on 127.0.0.1 port 8443, with a provider name of `provider name`, using a resolver certificate and associated key stored respectively in the `resolver.cert` and `resolver.key` files. The fifth optional parameter is a table of parameters" },
   { "addDOHLocal", true, "addr, certFile, keyFile [, urls [, vars]]", "listen to incoming DNS over HTTPS queries on the specified address using the specified certificate and key. The last two parameters are tables" },
   { "addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
+  { "addDynBlockSMT", true, "names, msessage[, seconds [, action]]", "block the set of names with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)" },
   { "addLocal", true, "addr [, {doTCP=true, reusePort=false, tcpFastOpenSize=0, interface=\"\", cpus={}}]", "add `addr` to the list of addresses we listen on" },
   { "addCacheHitResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a cache hit response rule" },
   { "addResponseAction", true, "DNS rule, DNS response action [, {uuid=\"UUID\"}]", "add a response rule" },
@@ -365,7 +367,9 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "DelayAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" },
   { "DelayResponseAction", true, "milliseconds", "delay the response by the specified amount of milliseconds (UDP-only)" },
   { "delta", true, "", "shows all commands entered that changed the configuration" },
+  { "DisableECSAction", true, "", "Disable the sending of ECS to the backend. Subsequent rules are processed after this action." },
   { "DisableValidationAction", true, "", "set the CD bit in the question, let it go through" },
+  { "DNSSECRule", true, "", "matches queries with the DO bit set" },
   { "DnstapLogAction", true, "identity, FrameStreamLogger [, alterFunction]", "send the contents of this query to a FrameStreamLogger or RemoteLogger as dnstap. `alterFunction` is a callback, receiving a DNSQuestion and a DnstapMessage, that can be used to modify the dnstap message" },
   { "DnstapLogResponseAction", true, "identity, FrameStreamLogger [, alterFunction]", "send the contents of this response to a remote or FrameStreamLogger or RemoteLogger as dnstap. `alterFunction` is a callback, receiving a DNSResponse and a DnstapMessage, that can be used to modify the dnstap message" },
   { "DropAction", true, "", "drop these packets" },
@@ -373,6 +377,8 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "DSTPortRule", true, "port", "matches questions received to the destination port specified" },
   { "dumpStats", true, "", "print all statistics we gather" },
   { "dynBlockRulesGroup", true, "", "return a new DynBlockRulesGroup object" },
+  { "ECSOverrideAction", true, "override", "Whether an existing EDNS Client Subnet value should be overridden (true) or not (false). Subsequent rules are processed after this action" },
+  { "ECSPrefixLengthAction", true, "v4, v6", "Set the ECS prefix length. Subsequent rules are processed after this action" },
   { "EDNSVersionRule", true, "version", "matches queries with the specified EDNS version" },
   { "EDNSOptionRule", true, "optcode", "matches queries with the specified EDNS0 option present" },
   { "ERCodeAction", true, "ercode", "Reply immediately by turning the query into a response with the specified EDNS extended rcode" },
@@ -386,6 +392,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "fixupCase", true, "bool", "if set (default to no), rewrite the first qname of the question part of the answer to match the one from the query. It is only useful when you have a downstream server that messes up the case of the question qname in the answer" },
   { "generateDNSCryptCertificate", true, "\"/path/to/providerPrivate.key\", \"/path/to/resolver.cert\", \"/path/to/resolver.key\", serial, validFrom, validUntil", "generate a new resolver private key and related certificate, valid from the `validFrom` timestamp until the `validUntil` one, signed with the provider private key" },
   { "generateDNSCryptProviderKeys", true, "\"/path/to/providerPublic.key\", \"/path/to/providerPrivate.key\"", "generate a new provider keypair" },
+  { "getAction", true, "n", "Returns the Action associated with rule n" },
   { "getBind", true, "n", "returns the listener at index n" },
   { "getDNSCryptBind", true, "n", "return the `DNSCryptContext` object corresponding to the bind `n`" },
   { "getDOHFrontend", true, "n", "returns the DOH frontend with index n" },
@@ -393,24 +400,40 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "getPoolServers", true, "pool", "return servers part of this pool" },
   { "getQueryCounters", true, "[max=10]", "show current buffer of query counters, limited by 'max' if provided" },
   { "getResponseRing", true, "", "return the current content of the response ring" },
+  { "getRespRing", true, "", "return the qname/rcode content of the response ring" },
   { "getServer", true, "n", "returns server with index n" },
   { "getServers", true, "", "returns a table with all defined servers" },
+  { "getStatisticsCounters", true, "", "returns a map of statistic counters" },
   { "getTLSContext", true, "n", "returns the TLS context with index n" },
   { "getTLSFrontend", true, "n", "returns the TLS frontend with index n" },
+  { "HTTPHeaderRule", true, "name, regex", "matches DoH queries with a HTTP header 'name' whose content matches the regular expression 'regex'"},
+  { "HTTPPathRule", true, "path", "matches DoH queries whose HTTP path is an exact match to 'path'"},
   { "inClientStartup", true, "", "returns true during console client parsing of configuration" },
+  { "includeDirectory", true, "path", "nclude configuration files from `path`" },
   { "grepq", true, "Netmask|DNS Name|100ms|{\"::1\", \"powerdns.com\", \"100ms\"} [, n]", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms" },
   { "leastOutstanding", false, "", "Send traffic to downstream server with least outstanding queries, with the lowest 'order', and within that the lowest recent latency"},
   { "LogAction", true, "[filename], [binary], [append], [buffered]", "Log a line for each query, to the specified file if any, to the console (require verbose) otherwise. When logging to a file, the `binary` optional parameter specifies whether we log in binary form (default) or in textual form, the `append` optional parameter specifies whether we open the file for appending or truncate each time (default), and the `buffered` optional parameter specifies whether writes to the file are buffered (default) or not." },
+  { "LuaAction", true, "function", "Invoke a Lua function that accepts a DNSQuestion" },
+  { "LuaResponseAction", true, "function", "Invoke a Lua function that accepts a DNSResponse" },
+  { "MacAddrAction", 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" },
+  { "makeIPCipherKey", true, "password", "generates a 16-byte key that can be used to pseudonymize IP addresses with IP cipher" },
   { "makeKey", true, "", "generate a new server access key, emit configuration line ready for pasting" },
+  { "makeRule", true, "rule", "Make a NetmaskGroupRule() or a SuffixMatchNodeRule(), depending on how it is called" }  ,
   { "MaxQPSIPRule", true, "qps, [v4Mask=32 [, v6Mask=64 [, burst=qps [, expiration=300 [, cleanupDelay=60]]]]]", "matches traffic exceeding the qps limit per subnet" },
   { "MaxQPSRule", true, "qps", "matches traffic **not** exceeding this qps limit" },
   { "mvCacheHitResponseRule", true, "from, to", "move cache hit response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
   { "mvResponseRule", true, "from, to", "move response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
   { "mvRule", true, "from, to", "move rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule, in which case the rule will be moved to the last position" },
   { "mvSelfAnsweredResponseRule", true, "from, to", "move self-answered response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule" },
+  { "NetmaskGroupRule", true, "nmg[, src]", "Matches traffic from/to the network range specified in nmg. Set the src parameter to false to match nmg against destination address instead of source address. This can be used to differentiate between clients" },
+  { "newBPFFilter", true, "maxV4, maxV6, maxQNames", "Return a new eBPF socket filter with a maximum of maxV4 IPv4, maxV6 IPv6 and maxQNames qname entries in the block table" },
+  { "newCA", true, "address", "Returns a ComboAddress based on `address`" },
   { "newDNSName", true, "name", "make a DNSName based on this .-terminated name" },
+  { "newDNSNameSet", true, "", "returns a new DNSNameSet" },
+  { "newDynBPFFilter", true, "bpf", "Return a new dynamic eBPF filter associated to a given BPF Filter" },
   { "newFrameStreamTcpLogger", true, "addr", "create a FrameStream logger object writing to a TCP address (addr should be ip:port), to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
   { "newFrameStreamUnixLogger", true, "socket", "create a FrameStream logger object writing to a local unix socket, to use with `DnstapLogAction()` and `DnstapLogResponseAction()`" },
+  { "newNMG", true, "", "Returns a NetmaskGroup" },
   { "newPacketCache", true, "maxEntries[, maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false, numberOfShards=1, deferrableInsertLock=true, options={}]", "return a new Packet Cache" },
   { "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" },
   { "newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`" },
@@ -418,16 +441,28 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "newServer", true, "{address=\"ip:port\", qps=1000, order=1, weight=10, pool=\"abuse\", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName=\"a.root-servers.net.\", checkType=\"A\", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source=\"address|interface name|address@interface\", sockets=1}", "instantiate a server" },
   { "newServerPolicy", true, "name, function", "create a policy object from a Lua function" },
   { "newSuffixMatchNode", true, "", "returns a new SuffixMatchNode" },
-  { "newDNSNameSet", true, "", "returns a new DNSNameSet" },
+  { "NoneAction", true, "", "Does nothing. Subsequent rules are processed after this action" },
   { "NoRecurseAction", true, "", "strip RD bit from the question, let it go through" },
+  { "NotRule", true, "selector", "Matches the traffic if the selector rule does not match" },
+  { "OpcodeRule", true, "code", "Matches queries with opcode code. code can be directly specified as an integer, or one of the built-in DNSOpcodes" },
+  { "OrRule", true, "selectors", "Matches the traffic if one or more of the the selectors rules does match" },
   { "PoolAction", true, "poolname", "set the packet into the specified pool" },
+  { "PoolAvailableRule", true, "poolname", "Check whether a pool has any servers available to handle queries" },
   { "printDNSCryptProviderFingerprint", true, "\"/path/to/providerPublic.key\"", "display the fingerprint of the provided resolver public key" },
+  { "ProbaRule", true, "probability", "Matches queries with a given probability. 1.0 means always" },
+  { "QClassRule", true, "qclass", "Matches queries with the specified qclass. class can be specified as an integer or as one of the built-in DNSClass" },
   { "QNameLabelsCountRule", true, "min, max", "matches if the qname has less than `min` or more than `max` labels" },
   { "QNameRule", true, "qname", "matches queries with the specified qname" },
+  { "QNameSetRule", true, "set", "Matches if the set contains exact qname" },
   { "QNameWireLengthRule", true, "min, max", "matches if the qname's length on the wire is less than `min` or more than `max` bytes" },
+  { "QPSAction", true, "maxqps", "Drop a packet if it does exceed the maxqps queries per second limits. Letting the subsequent rules apply otherwise" },
+  { "QPSPoolAction", true, "maxqps, poolname", "Send the packet into the specified pool only if it does not exceed the maxqps queries per second limits. Letting the subsequent rules apply otherwise" },
   { "QTypeRule", true, "qtype", "matches queries with the specified qtype" },
   { "RCodeAction", true, "rcode", "Reply immediately by turning the query into a response with the specified rcode" },
   { "RCodeRule", true, "rcode", "matches responses with the specified rcode" },
+  { "RDRule", true, "", "Matches queries with the RD flag set" },
+  { "RecordsCountRule", true, "section, minCount, maxCount", "Matches if there is at least minCount and at most maxCount records in the section section. section can be specified as an integer or as a DNS Packet Sections" },
+  { "RecordsTypeCountRule", true, "section, qtype, minCount, maxCount", "Matches if there is at least minCount and at most maxCount records of type type in the section section" },
   { "RegexRule", true, "regex", "matches the query name against the supplied regex" },
   { "registerDynBPFFilter", true, "DynBPFFilter", "register this dynamic BPF filter into the web interface so that its counters are displayed" },
   { "reloadAllCertificates", true, "", "reload all DNSCrypt and TLS certificates, along with their associated keys" },
@@ -444,11 +479,14 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "setAddEDNSToSelfGeneratedResponses", true, "add", "set whether to add EDNS to self-generated responses, provided that the initial query had EDNS" },
   { "setAllowEmptyResponse", true, "allow", "Set to true (defaults to false) to allow empty responses (qdcount=0) with a NoError or NXDomain rcode (default) from backends" },
   { "setAPIWritable", true, "bool, dir", "allow modifications via the API. if `dir` is set, it must be a valid directory where the configuration files will be written by the API" },
+  { "setCacheCleaningDelay", true, "num", "Set the interval in seconds between two runs of the cache cleaning algorithm, removing expired entries" },
+  { "setCacheCleaningPercentage", true, "num", "Set the percentage of the cache that the cache cleaning algorithm will try to free by removing expired entries. By default (100), all expired entries are remove" },
   { "setConsoleACL", true, "{netmask, netmask}", "replace the console ACL set with these netmasks" },
   { "setConsoleConnectionsLogging", true, "enabled", "whether to log the opening and closing of console connections" },
   { "setConsoleOutputMaxMsgSize", true, "messageSize", "set console message maximum size in bytes, default is 10 MB" },
-  { "setDNSSECPool", true, "pool name", "move queries requesting DNSSEC processing to this pool" },
+  { "setDefaultBPFFilter", true, "filter", "When used at configuration time, the corresponding BPFFilter will be attached to every bind" },
   { "setDynBlocksAction", true, "action", "set which action is performed when a query is blocked. Only DNSAction.Drop (the default) and DNSAction.Refused are supported" },
+  { "SetECSAction", true, "v4[, v6]", "Set the ECS prefix and prefix length sent to backends to an arbitrary value" },
   { "setECSOverride", true, "bool", "whether to override an existing EDNS Client Subnet value in the query" },
   { "setECSSourcePrefixV4", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv4 queries" },
   { "setECSSourcePrefixV6", true, "prefix-length", "the EDNS Client Subnet prefix-length used for IPv6 queries" },
@@ -462,12 +500,13 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "setMaxUDPOutstanding", true, "n", "set the maximum number of outstanding UDP queries to a given backend server. This can only be set at configuration time and defaults to 10240" },
   { "setPayloadSizeOnSelfGeneratedAnswers", true, "payloadSize", "set the UDP payload size advertised via EDNS on self-generated responses" },
   { "setPoolServerPolicy", true, "policy, pool", "set the server selection policy for this pool to that policy" },
-  { "setPoolServerPolicy", true, "name, func, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" },
+  { "setPoolServerPolicyLua", true, "name, func, pool", "set the server selection policy for this pool to one named 'name' and provided by 'function'" },
   { "setPreserveTrailingData", true, "bool", "set whether trailing data should be preserved while adding ECS or XPF records to incoming queries" },
   { "setQueryCount", true, "bool", "set whether queries should be counted" },
   { "setQueryCountFilter", true, "func", "filter queries that would be counted, where `func` is a function with parameter `dq` which decides whether a query should and how it should be counted" },
   { "setRingBuffersLockRetries", true, "n", "set the number of attempts to get a non-blocking lock to a ringbuffer shard before blocking" },
   { "setRingBuffersSize", true, "n [, numberOfShards]", "set the capacity of the ringbuffers used for live traffic inspection to `n`, and optionally the number of shards to use to `numberOfShards`" },
+  { "setRoundRobinFailOnNoServer", true, "value", "By default the roundrobin load-balancing policy will still try to select a backend even if all backends are currently down. Setting this to true will make the policy fail and return that no server is available instead" },
   { "setRules", true, "list of rules", "replace the current rules with the supplied list of pairs of DNS Rules and DNS Actions (see `newRuleAction()`)" },
   { "setSecurityPollInterval", true, "n", "set the security polling interval to `n` seconds" },
   { "setSecurityPollSuffix", true, "suffix", "set the security polling suffix to the specified value" },
@@ -484,6 +523,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "setUDPTimeout", true, "n", "set the maximum time dnsdist will wait for a response from a backend over UDP, in seconds" },
   { "setVerboseHealthChecks", true, "bool", "set whether health check errors will be logged" },
   { "setWebserverConfig", true, "[{password=string, apiKey=string, customHeaders}]", "Updates webserver configuration" },
+  { "setWHashedPertubation", true, "value", "Set the hash perturbation value to be used in the whashed policy instead of a random one, allowing to have consistent whashed results on different instance" },
   { "show", true, "string", "outputs `string`" },
   { "showACL", true, "", "show our ACL set" },
   { "showBinds", true, "", "show listening addresses (frontends)" },
@@ -505,14 +545,19 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "showTLSContexts", true, "", "list all the available TLS contexts" },
   { "showVersion", true, "", "show the current version" },
   { "shutdown", true, "", "shut down `dnsdist`" },
+  { "SkipCacheAction", true, "", "Don’t lookup the cache for this query, don’t store the answer" },
+  { "SNIRule", true, "name", "Create a rule which matches on the incoming TLS SNI value, if any (DoT or DoH)" },
   { "snmpAgent", true, "enableTraps [, masterSocket]", "enable `SNMP` support. `enableTraps` is a boolean indicating whether traps should be sent and `masterSocket` an optional string specifying how to connect to the master agent"},
   { "SNMPTrapAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the query description"},
   { "SNMPTrapResponseAction", true, "[reason]", "send an SNMP trap, adding the optional `reason` string as the response description"},
   { "SpoofAction", true, "{ip, ...} ", "forge a response with the specified IPv4 (for an A query) or IPv6 (for an AAAA). If you specify multiple addresses, all that match the query type (A, AAAA or ANY) will get spoofed in" },
+  { "SpoofCNAMEAction", true, "cname", "Forge a response with the specified CNAME value" },
+  { "SuffixMatchNodeRule", true, "smn[, quiet]", "Matches based on a group of domain suffixes for rapid testing of membership. Pass true as second parameter to prevent listing of all domains matched" },
   { "TagAction", true, "name, value", "set the tag named 'name' to the given value" },
   { "TagResponseAction", true, "name, value", "set the tag named 'name' to the given value" },
   { "TagRule", true, "name [, value]", "matches if the tag named 'name' is present, with the given 'value' matching if any" },
   { "TCAction", true, "", "create answer to query with TC and RD bits set, to move to TCP" },
+  { "TCPRule", true, "[tcp]", "Matches question received over TCP if tcp is true, over UDP otherwise" },
   { "TeeAction", true, "remote [, addECS]", "send copy of query to remote, optionally adding ECS info" },
   { "TempFailureCacheTTLAction", true, "ttl", "set packetcache TTL for temporary failure replies" },
   { "testCrypto", true, "", "test of the crypto all works" },
@@ -522,11 +567,11 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "topClients", true, "n", "show top-`n` clients sending the most queries over length of ringbuffer" },
   { "topQueries", true, "n[, labels]", "show top 'n' queries, as grouped when optionally cut down to 'labels' labels" },
   { "topResponses", true, "n, kind[, labels]", "show top 'n' responses with RCODE=kind (0=NO Error, 2=ServFail, 3=ServFail), as grouped when optionally cut down to 'labels' labels" },
-  { "topCacheHitResponseRule", true, "", "move the last cache hit response rule to the first position" },
   { "topResponseRule", true, "", "move the last response rule to the first position" },
   { "topRule", true, "", "move the last rule to the first position" },
   { "topSelfAnsweredResponseRule", true, "", "move the last self-answered response rule to the first position" },
   { "topSlow", true, "[top][, limit][, labels]", "show `top` queries slower than `limit` milliseconds, grouped by last `labels` labels" },
+  { "TrailingDataRule", true, "", "Matches if the query has trailing data" },
   { "truncateTC", true, "bool", "if set (defaults to no starting with dnsdist 1.2.0) truncate TC=1 answers so they are actually empty. Fixes an issue for PowerDNS Authoritative Server 2.9.22. Note: turning this on breaks compatibility with RFC 6891." },
   { "unregisterDynBPFFilter", true, "DynBPFFilter", "unregister this dynamic BPF filter" },
   { "webserver", true, "address:port, password [, apiKey [, customHeaders ]])", "launch a webserver with stats on that address with that password" },
index c9cadfc5ee01ea23fc2c3e8b77e8ce683c3a2f90..2943ad31899885cd2379db0a4037fc32f3568c79 100644 (file)
 class DropAction : public DNSAction
 {
 public:
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     return Action::Drop;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "drop";
   }
@@ -54,11 +54,11 @@ public:
 class AllowAction : public DNSAction
 {
 public:
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     return Action::Allow;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "allow";
   }
@@ -67,11 +67,11 @@ public:
 class NoneAction : public DNSAction
 {
 public:
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "no op";
   }
@@ -82,14 +82,14 @@ class QPSAction : public DNSAction
 public:
   QPSAction(int limit) : d_qps(limit, limit)
   {}
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     if(d_qps.check())
       return Action::None;
     else
       return Action::Drop;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "qps limit to "+std::to_string(d_qps.getRate());
   }
@@ -102,12 +102,12 @@ class DelayAction : public DNSAction
 public:
   DelayAction(int msec) : d_msec(msec)
   {}
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     *ruleresult=std::to_string(d_msec);
     return Action::Delay;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "delay by "+std::to_string(d_msec)+ " msec";
   }
@@ -121,9 +121,9 @@ class TeeAction : public DNSAction
 public:
   TeeAction(const ComboAddress& ca, bool addECS=false);
   ~TeeAction() override;
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override;
-  string toString() const override;
-  std::map<string, double> getStats() const override;
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override;
+  std::string toString() const override;
+  std::map<std::string, double> getStats() const override;
 
 private:
   ComboAddress d_remote;
@@ -162,7 +162,7 @@ TeeAction::~TeeAction()
   d_worker.join();
 }
 
-DNSAction::Action TeeAction::operator()(DNSQuestion* dq, string* ruleresult) const
+DNSAction::Action TeeAction::operator()(DNSQuestion* dq, std::string* ruleresult) const
 {
   if(dq->tcp) {
     d_tcpdrops++;
@@ -179,7 +179,7 @@ DNSAction::Action TeeAction::operator()(DNSQuestion* dq, string* ruleresult) con
       query.reserve(dq->size);
       query.assign((char*) dq->dh, len);
 
-      string newECSOption;
+      std::string newECSOption;
       generateECSOption(dq->ecsSet ? dq->ecs.getNetwork() : *dq->remote, newECSOption, dq->ecsSet ? dq->ecs.getBits() :  dq->ecsPrefixLength);
 
       if (!handleEDNSClientSubnet(const_cast<char*>(query.c_str()), query.capacity(), dq->qname->wirelength(), &len, &ednsAdded, &ecsAdded, dq->ecsOverride, newECSOption, g_preserveTrailingData)) {
@@ -198,12 +198,12 @@ DNSAction::Action TeeAction::operator()(DNSQuestion* dq, string* ruleresult) con
   return DNSAction::Action::None;
 }
 
-string TeeAction::toString() const
+std::string TeeAction::toString() const
 {
   return "tee to "+d_remote.toStringWithPort();
 }
 
-std::map<string,double> TeeAction::getStats() const
+std::map<std::string,double> TeeAction::getStats() const
 {
   return {{"queries", d_queries},
           {"responses", d_responses},
@@ -259,18 +259,18 @@ class PoolAction : public DNSAction
 {
 public:
   PoolAction(const std::string& pool) : d_pool(pool) {}
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     *ruleresult=d_pool;
     return Action::Pool;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "to pool "+d_pool;
   }
 
 private:
-  string d_pool;
+  std::string d_pool;
 };
 
 
@@ -278,7 +278,7 @@ class QPSPoolAction : public DNSAction
 {
 public:
   QPSPoolAction(unsigned int limit, const std::string& pool) : d_qps(limit, limit), d_pool(pool) {}
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     if(d_qps.check()) {
       *ruleresult=d_pool;
@@ -287,27 +287,27 @@ public:
     else
       return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "max " +std::to_string(d_qps.getRate())+" to pool "+d_pool;
   }
 
 private:
   QPSLimiter d_qps;
-  string d_pool;
+  std::string d_pool;
 };
 
 class RCodeAction : public DNSAction
 {
 public:
   RCodeAction(uint8_t rcode) : d_rcode(rcode) {}
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     dq->dh->rcode = d_rcode;
     dq->dh->qr = true; // for good measure
     return Action::HeaderModify;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "set rcode "+std::to_string(d_rcode);
   }
@@ -320,14 +320,14 @@ class ERCodeAction : public DNSAction
 {
 public:
   ERCodeAction(uint8_t rcode) : d_rcode(rcode) {}
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     dq->dh->rcode = (d_rcode & 0xF);
     dq->ednsRCode = ((d_rcode & 0xFFF0) >> 4);
     dq->dh->qr = true; // for good measure
     return Action::HeaderModify;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "set ercode "+ERCode::to_s(d_rcode);
   }
@@ -339,23 +339,23 @@ private:
 class TCAction : public DNSAction
 {
 public:
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     return Action::Truncate;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "tc=1 answer";
   }
 };
 
-DNSAction::Action LuaAction::operator()(DNSQuestion* dq, string* ruleresult) const
+DNSAction::Action LuaAction::operator()(DNSQuestion* dq, std::string* ruleresult) const
 {
   std::lock_guard<std::mutex> lock(g_luamutex);
   try {
     auto ret = d_func(dq);
     if (ruleresult) {
-      if (boost::optional<string> rule = std::get<1>(ret)) {
+      if (boost::optional<std::string> rule = std::get<1>(ret)) {
         *ruleresult = *rule;
       }
       else {
@@ -372,13 +372,13 @@ DNSAction::Action LuaAction::operator()(DNSQuestion* dq, string* ruleresult) con
   return DNSAction::Action::ServFail;
 }
 
-DNSResponseAction::Action LuaResponseAction::operator()(DNSResponse* dr, string* ruleresult) const
+DNSResponseAction::Action LuaResponseAction::operator()(DNSResponse* dr, std::string* ruleresult) const
 {
   std::lock_guard<std::mutex> lock(g_luamutex);
   try {
     auto ret = d_func(dr);
     if(ruleresult) {
-      if (boost::optional<string> rule = std::get<1>(ret)) {
+      if (boost::optional<std::string> rule = std::get<1>(ret)) {
         *ruleresult = *rule;
       }
       else {
@@ -395,7 +395,7 @@ DNSResponseAction::Action LuaResponseAction::operator()(DNSResponse* dr, string*
   return DNSResponseAction::Action::ServFail;
 }
 
-DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, string* ruleresult) const
+DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, std::string* ruleresult) const
 {
   uint16_t qtype = dq->qtype;
   // do we even have a response?
@@ -449,7 +449,7 @@ DNSAction::Action SpoofAction::operator()(DNSQuestion* dq, string* ruleresult) c
   dq->dh->arcount = 0; // for now, forget about your EDNS, we're marching over it
 
   if(qtype == QType::CNAME) {
-    string wireData = d_cname.toDNSString(); // Note! This doesn't do compression!
+    std::string wireData = d_cname.toDNSString(); // Note! This doesn't do compression!
     const unsigned char recordstart[]={0xc0, 0x0c,    // compressed name
                                        0, (unsigned char) qtype,
                                        0, QClass::IN, // IN
@@ -499,19 +499,19 @@ class MacAddrAction : public DNSAction
 public:
   MacAddrAction(uint16_t code) : d_code(code)
   {}
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     if(dq->dh->arcount)
       return Action::None;
 
-    string mac = getMACAddress(*dq->remote);
+    std::string mac = getMACAddress(*dq->remote);
     if(mac.empty())
       return Action::None;
 
-    string optRData;
+    std::string optRData;
     generateEDNSOption(d_code, mac, optRData);
 
-    string res;
+    std::string res;
     generateOptRR(optRData, res, g_EdnsUDPPayloadSize, 0, false);
 
     if ((dq->size - dq->len) < res.length())
@@ -524,7 +524,7 @@ public:
 
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "add EDNS MAC (code="+std::to_string(d_code)+")";
   }
@@ -535,12 +535,12 @@ private:
 class NoRecurseAction : public DNSAction
 {
 public:
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     dq->dh->rd = false;
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "set rd=0";
   }
@@ -561,7 +561,7 @@ public:
     else
       d_fp = fopen(str.c_str(), "w");
     if(!d_fp)
-      throw std::runtime_error("Unable to open file '"+str+"' for logging: "+string(strerror(errno)));
+      throw std::runtime_error("Unable to open file '"+str+"' for logging: "+std::string(strerror(errno)));
     if(!buffered)
       setbuf(d_fp, 0);
   }
@@ -570,14 +570,14 @@ public:
     if(d_fp)
       fclose(d_fp);
   }
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     if(!d_fp) {
       vinfolog("Packet from %s for %s %s with id %d", dq->remote->toStringWithPort(), dq->qname->toString(), QType(dq->qtype).getName(), dq->dh->id);
     }
     else {
       if(d_binary) {
-        string out = dq->qname->toDNSString();
+        std::string out = dq->qname->toDNSString();
         fwrite(out.c_str(), 1, out.size(), d_fp);
         fwrite((void*)&dq->qtype, 1, 2, d_fp);
       }
@@ -587,7 +587,7 @@ public:
     }
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     if (!d_fname.empty()) {
       return "log to " + d_fname;
@@ -595,7 +595,7 @@ public:
     return "log";
   }
 private:
-  string d_fname;
+  std::string d_fname;
   FILE* d_fp{0};
   bool d_binary{true};
 };
@@ -604,12 +604,12 @@ private:
 class DisableValidationAction : public DNSAction
 {
 public:
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     dq->dh->cd = true;
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "set cd=1";
   }
@@ -618,12 +618,12 @@ public:
 class SkipCacheAction : public DNSAction
 {
 public:
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     dq->skipCache = true;
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "skip cache";
   }
@@ -634,12 +634,12 @@ class TempFailureCacheTTLAction : public DNSAction
 public:
   TempFailureCacheTTLAction(uint32_t ttl) : d_ttl(ttl)
   {}
-  TempFailureCacheTTLAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  TempFailureCacheTTLAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     dq->tempFailureTTL = d_ttl;
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "set tempfailure cache ttl to "+std::to_string(d_ttl);
   }
@@ -653,12 +653,12 @@ public:
   ECSPrefixLengthAction(uint16_t v4Length, uint16_t v6Length) : d_v4PrefixLength(v4Length), d_v6PrefixLength(v6Length)
   {
   }
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     dq->ecsPrefixLength = dq->remote->sin4.sin_family == AF_INET ? d_v4PrefixLength : d_v6PrefixLength;
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "set ECS prefix length to " + std::to_string(d_v4PrefixLength) + "/" + std::to_string(d_v6PrefixLength);
   }
@@ -673,12 +673,12 @@ public:
   ECSOverrideAction(bool ecsOverride) : d_ecsOverride(ecsOverride)
   {
   }
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     dq->ecsOverride = d_ecsOverride;
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "set ECS override to " + std::to_string(d_ecsOverride);
   }
@@ -690,12 +690,12 @@ private:
 class DisableECSAction : public DNSAction
 {
 public:
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     dq->useECS = false;
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "disable ECS";
   }
@@ -712,7 +712,7 @@ public:
   {
   }
 
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     dq->ecsSet = true;
 
@@ -726,9 +726,9 @@ public:
     return Action::None;
   }
 
-  string toString() const override
+  std::string toString() const override
   {
-    string result = "set ECS to " + d_v4.toString();
+    std::string result = "set ECS to " + d_v4.toString();
     if (d_hasV6) {
       result += " / " + d_v6.toString();
     }
@@ -748,7 +748,7 @@ public:
   DnstapLogAction(const std::string& identity, std::shared_ptr<RemoteLoggerInterface>& logger, boost::optional<std::function<void(DNSQuestion*, DnstapMessage*)> > alterFunc): d_identity(identity), d_logger(logger), d_alterFunc(alterFunc)
   {
   }
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
 #ifdef HAVE_PROTOBUF
     DnstapMessage message(d_identity, dq->remote, dq->local, dq->tcp, reinterpret_cast<const char*>(dq->dh), dq->len, dq->queryTime, nullptr);
@@ -764,7 +764,7 @@ public:
 #endif /* HAVE_PROTOBUF */
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "remote log as dnstap to " + (d_logger ? d_logger->toString() : "");
   }
@@ -780,7 +780,7 @@ public:
   RemoteLogAction(std::shared_ptr<RemoteLoggerInterface>& logger, boost::optional<std::function<void(DNSQuestion*, DNSDistProtoBufMessage*)> > alterFunc, const std::string& serverID, const std::string& ipEncryptKey): d_logger(logger), d_alterFunc(alterFunc), d_serverID(serverID), d_ipEncryptKey(ipEncryptKey)
   {
   }
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
 #ifdef HAVE_PROTOBUF
     if (!dq->uniqueId) {
@@ -810,7 +810,7 @@ public:
 #endif /* HAVE_PROTOBUF */
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "remote log to " + (d_logger ? d_logger->toString() : "");
   }
@@ -827,7 +827,7 @@ public:
   SNMPTrapAction(const std::string& reason): d_reason(reason)
   {
   }
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     if (g_snmpAgent && g_snmpTrapsEnabled) {
       g_snmpAgent->sendDNSTrap(*dq, d_reason);
@@ -835,7 +835,7 @@ public:
 
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "send SNMP trap";
   }
@@ -849,7 +849,7 @@ public:
   TagAction(const std::string& tag, const std::string& value): d_tag(tag), d_value(value)
   {
   }
-  DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override
+  DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override
   {
     if (!dq->qTag) {
       dq->qTag = std::make_shared<QTag>();
@@ -859,7 +859,7 @@ public:
 
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "set tag '" + d_tag + "' to value '" + d_value + "'";
   }
@@ -874,7 +874,7 @@ public:
   DnstapLogResponseAction(const std::string& identity, std::shared_ptr<RemoteLoggerInterface>& logger, boost::optional<std::function<void(DNSResponse*, DnstapMessage*)> > alterFunc): d_identity(identity), d_logger(logger), d_alterFunc(alterFunc)
   {
   }
-  DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+  DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
   {
 #ifdef HAVE_PROTOBUF
     struct timespec now;
@@ -892,7 +892,7 @@ public:
 #endif /* HAVE_PROTOBUF */
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "log response as dnstap to " + (d_logger ? d_logger->toString() : "");
   }
@@ -908,7 +908,7 @@ public:
   RemoteLogResponseAction(std::shared_ptr<RemoteLoggerInterface>& logger, boost::optional<std::function<void(DNSResponse*, DNSDistProtoBufMessage*)> > alterFunc, const std::string& serverID, const std::string& ipEncryptKey, bool includeCNAME): d_logger(logger), d_alterFunc(alterFunc), d_serverID(serverID), d_ipEncryptKey(ipEncryptKey), d_includeCNAME(includeCNAME)
   {
   }
-  DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+  DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
   {
 #ifdef HAVE_PROTOBUF
     if (!dr->uniqueId) {
@@ -938,7 +938,7 @@ public:
 #endif /* HAVE_PROTOBUF */
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "remote log response to " + (d_logger ? d_logger->toString() : "");
   }
@@ -953,11 +953,11 @@ private:
 class DropResponseAction : public DNSResponseAction
 {
 public:
-  DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+  DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
   {
     return Action::Drop;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "drop";
   }
@@ -966,11 +966,11 @@ public:
 class AllowResponseAction : public DNSResponseAction
 {
 public:
-  DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+  DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
   {
     return Action::Allow;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "allow";
   }
@@ -981,12 +981,12 @@ class DelayResponseAction : public DNSResponseAction
 public:
   DelayResponseAction(int msec) : d_msec(msec)
   {}
-  DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+  DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
   {
     *ruleresult=std::to_string(d_msec);
     return Action::Delay;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "delay by "+std::to_string(d_msec)+ " msec";
   }
@@ -1000,7 +1000,7 @@ public:
   SNMPTrapResponseAction(const std::string& reason): d_reason(reason)
   {
   }
-  DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+  DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
   {
     if (g_snmpAgent && g_snmpTrapsEnabled) {
       g_snmpAgent->sendDNSTrap(*dr, d_reason);
@@ -1008,7 +1008,7 @@ public:
 
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "send SNMP trap";
   }
@@ -1022,7 +1022,7 @@ public:
   TagResponseAction(const std::string& tag, const std::string& value): d_tag(tag), d_value(value)
   {
   }
-  DNSResponseAction::Action operator()(DNSResponse* dr, string* ruleresult) const override
+  DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override
   {
     if (!dr->qTag) {
       dr->qTag = std::make_shared<QTag>();
@@ -1032,7 +1032,7 @@ public:
 
     return Action::None;
   }
-  string toString() const override
+  std::string toString() const override
   {
     return "set tag '" + d_tag + "' to value '" + d_value + "'";
   }
@@ -1135,7 +1135,7 @@ void setupLuaActions()
       return std::shared_ptr<DNSAction>(new MacAddrAction(code));
     });
 
-  g_lua.writeFunction("PoolAction", [](const string& a) {
+  g_lua.writeFunction("PoolAction", [](const std::string& a) {
       return std::shared_ptr<DNSAction>(new PoolAction(a));
     });
 
@@ -1143,16 +1143,16 @@ void setupLuaActions()
       return std::shared_ptr<DNSAction>(new QPSAction(limit));
     });
 
-  g_lua.writeFunction("QPSPoolAction", [](int limit, const string& a) {
+  g_lua.writeFunction("QPSPoolAction", [](int limit, const std::string& a) {
       return std::shared_ptr<DNSAction>(new QPSPoolAction(limit, a));
     });
 
-  g_lua.writeFunction("SpoofAction", [](boost::variant<string,vector<pair<int, string>>> inp, boost::optional<string> b ) {
+  g_lua.writeFunction("SpoofAction", [](boost::variant<std::string,vector<pair<int, std::string>>> inp, boost::optional<std::string> b ) {
       vector<ComboAddress> addrs;
-      if(auto s = boost::get<string>(&inp))
+      if(auto s = boost::get<std::string>(&inp))
         addrs.push_back(ComboAddress(*s));
       else {
-        const auto& v = boost::get<vector<pair<int,string>>>(inp);
+        const auto& v = boost::get<vector<pair<int,std::string>>>(inp);
         for(const auto& a: v)
           addrs.push_back(ComboAddress(a.second));
       }
@@ -1161,7 +1161,7 @@ void setupLuaActions()
       return std::shared_ptr<DNSAction>(new SpoofAction(addrs));
     });
 
-  g_lua.writeFunction("SpoofCNAMEAction", [](const string& a) {
+  g_lua.writeFunction("SpoofCNAMEAction", [](const std::string& a) {
       return std::shared_ptr<DNSAction>(new SpoofAction(a));
     });
 
index dbbb6cdba95ea2f0c08d4a0d8308d9618f725053..1392397e89c71874d3eb0ec67172a07182d1ab88 100644 (file)
@@ -85,6 +85,10 @@ void setupLuaBindingsDNSQuestion()
       return true;
     });
 
+  g_lua.registerFunction<std::string(DNSQuestion::*)()>("getServerNameIndication", [](const DNSQuestion& dq) {
+      return dq.sni;
+    });
+
   g_lua.registerFunction<void(DNSQuestion::*)(std::string)>("sendTrap", [](const DNSQuestion& dq, boost::optional<std::string> reason) {
 #ifdef HAVE_NET_SNMP
       if (g_snmpAgent && g_snmpTrapsEnabled) {
index 5fbf7643dbb19ef4e2fe312b94cee25d7538674b..b781e8c79ac5ecef92b8e0b98dc95ea2e9015cb6 100644 (file)
@@ -295,6 +295,10 @@ void setupLuaRules()
     });
 #endif
 
+  g_lua.writeFunction("SNIRule", [](const std::string& name) {
+      return std::shared_ptr<DNSRule>(new SNIRule(name));
+  });
+
   g_lua.writeFunction("SuffixMatchNodeRule", [](const SuffixMatchNode& smn, boost::optional<bool> quiet) {
       return std::shared_ptr<DNSRule>(new SuffixMatchNodeRule(smn, quiet ? *quiet : false));
     });
index b912cda51b4ef6028a3e3cd8cd02e479841035eb..f10c6c3d37ba72890329202404e5bdcdb389494d 100644 (file)
@@ -142,17 +142,4 @@ void setupLuaVars()
         { "VERSION2", DNSCryptExchangeVersion::VERSION2 },
     });
 #endif
-
-  g_lua.writeVariable("EDNSOptionCode", std::unordered_map<string, uint8_t>{
-      { "NSID", EDNSOptionCode::NSID },
-      { "DAU", EDNSOptionCode::DAU },
-      { "DHU", EDNSOptionCode::DHU },
-      { "N3U", EDNSOptionCode::N3U },
-      { "ECS", EDNSOptionCode::ECS },
-      { "EXPIRE", EDNSOptionCode::EXPIRE },
-      { "COOKIE", EDNSOptionCode::COOKIE },
-      { "TCPKEEPALIVE", EDNSOptionCode::TCPKEEPALIVE },
-      { "PADDING", EDNSOptionCode::PADDING },
-      { "CHAIN", EDNSOptionCode::CHAIN }
-    });
 }
index 1c51cdc16cab584b5df2f00c31c1262a877c61d0..8419011661fd5022e8df2547b6c7d906652bcb72 100644 (file)
@@ -440,6 +440,10 @@ public:
         catch(const FDMultiplexerException& e) {
           vinfolog("Got an exception when trying to remove a pending IO operation on the socket to the %s backend: %s", d_ds->getName(), e.what());
         }
+        catch(const std::runtime_error& e) {
+          /* might be thrown by getHandle() */
+          vinfolog("Got an exception when trying to remove a pending IO operation on the socket to the %s backend: %s", d_ds->getName(), e.what());
+        }
       }
     }
 
@@ -804,6 +808,7 @@ static void handleQuery(std::shared_ptr<IncomingTCPConnectionState>& state, stru
   DNSName qname(query, state->d_querySize, sizeof(dnsheader), false, &qtype, &qclass, &consumed);
   DNSQuestion dq(&qname, qtype, qclass, consumed, &state->d_ids.origDest, &state->d_ci.remote, reinterpret_cast<dnsheader*>(query), state->d_buffer.size(), state->d_querySize, true, &queryRealTime);
   dq.dnsCryptQuery = std::move(dnsCryptQuery);
+  dq.sni = state->d_handler.getServerNameIndication();
 
   state->d_isXFR = (dq.qtype == QType::AXFR || dq.qtype == QType::IXFR);
   if (state->d_isXFR) {
@@ -901,7 +906,7 @@ static void handleDownstreamIO(std::shared_ptr<IncomingTCPConnectionState>& stat
       }
 #endif /* MSG_FASTOPEN */
 
-      size_t sent = sendMsgWithTimeout(fd, reinterpret_cast<const char *>(&state->d_buffer.at(state->d_currentPos)), state->d_buffer.size() - state->d_currentPos, 0, &state->d_ds->remote, &state->d_ds->sourceAddr, state->d_ds->sourceItf, 0, socketFlags);
+      size_t sent = sendMsgWithOptions(fd, reinterpret_cast<const char *>(&state->d_buffer.at(state->d_currentPos)), state->d_buffer.size() - state->d_currentPos, &state->d_ds->remote, &state->d_ds->sourceAddr, state->d_ds->sourceItf, socketFlags);
       if (sent == state->d_buffer.size()) {
         /* request sent ! */
         state->d_downstreamConnection->incQueries();
index 704c575172a0533690e185b4ea777bc56f7c635d..b9faa558374723b6f7ea69fe81643f8d628d3211 100644 (file)
@@ -443,6 +443,22 @@ static void connectionThread(int sock, ComboAddress remote)
           output << "\n";
         }
 
+        // Latency histogram buckets
+        output << "# HELP dnsdist_latency Histogram of responses by latency\n";
+        output << "# TYPE dnsdist_latency histogram\n";
+        uint64_t latency_amounts = g_stats.latency0_1;
+        output << "dnsdist_latency_bucket{le=\"1\"} " << latency_amounts << "\n";
+        latency_amounts += g_stats.latency1_10;
+        output << "dnsdist_latency_bucket{le=\"10\"} " << latency_amounts << "\n";
+        latency_amounts += g_stats.latency10_50;
+        output << "dnsdist_latency_bucket{le=\"50\"} " << latency_amounts << "\n";
+        latency_amounts += g_stats.latency50_100;
+        output << "dnsdist_latency_bucket{le=\"100\"} " << latency_amounts << "\n";
+        latency_amounts += g_stats.latency100_1000;
+        output << "dnsdist_latency_bucket{le=\"1000\"} " << latency_amounts << "\n";
+        latency_amounts += g_stats.latencySlow; // Should be the same as latency_count
+        output << "dnsdist_latency_bucket{le=\"+Inf\"} " << latency_amounts << "\n";
+
         auto states = g_dstates.getLocal();
         const string statesbase = "dnsdist_server_";
 
index deea14b769773f257d3de6990addd65c2d4c1e97..d5c17029a986ca9f5ebabb205317182dcd1f3a8a 100644 (file)
@@ -200,6 +200,7 @@ void doLatencyStats(double udiff)
   else if(udiff < 100000) ++g_stats.latency50_100;
   else if(udiff < 1000000) ++g_stats.latency100_1000;
   else ++g_stats.latencySlow;
+  g_stats.latencySum += udiff / 1000;
 
   auto doAvg = [](double& var, double n, double weight) {
     var = (weight -1) * var/weight + n/weight;
@@ -1668,7 +1669,7 @@ static void MultipleMessagesUDPClientThread(ClientState* cs, LocalHolders& holde
       unsigned int got = msgVec[msgIdx].msg_len;
       const ComboAddress& remote = recvData[msgIdx].remote;
 
-      if (got < 0 || static_cast<size_t>(got) < sizeof(struct dnsheader)) {
+      if (static_cast<size_t>(got) < sizeof(struct dnsheader)) {
         ++g_stats.nonCompliantQueries;
         continue;
       }
@@ -2792,3 +2793,8 @@ catch(PDNSException &ae)
   errlog("Fatal pdns error: %s", ae.reason);
   _exit(EXIT_FAILURE);
 }
+
+uint64_t getLatencyCount(const std::string&)
+{
+    return g_stats.responses + g_stats.selfAnswered + g_stats.cacheHits;
+}
index 3d32ee2a10466a689beb77180f1a28b34bc8a142..a96a87b439d17ff920bc7e65eb0ae8d642b9eb99 100644 (file)
@@ -75,6 +75,7 @@ struct DNSQuestion
 #endif
   Netmask ecs;
   boost::optional<Netmask> subnet;
+  std::string sni; /* Server Name Indication, if any (DoT or DoH) */
   const DNSName* qname{nullptr};
   const ComboAddress* local{nullptr};
   const ComboAddress* remote{nullptr};
@@ -224,6 +225,8 @@ extern GlobalStateHolder<NetmaskTree<DynBlock>> g_dynblockNMG;
 
 extern vector<pair<struct timeval, std::string> > g_confDelta;
 
+extern uint64_t getLatencyCount(const std::string&);
+
 struct DNSDistStats
 {
   using stat_t=std::atomic<uint64_t>; // aww yiss ;-)
@@ -250,7 +253,7 @@ struct DNSDistStats
   stat_t noPolicy{0};
   stat_t cacheHits{0};
   stat_t cacheMisses{0};
-  stat_t latency0_1{0}, latency1_10{0}, latency10_50{0}, latency50_100{0}, latency100_1000{0}, latencySlow{0};
+  stat_t latency0_1{0}, latency1_10{0}, latency10_50{0}, latency50_100{0}, latency100_1000{0}, latencySlow{0}, latencySum{0};
   stat_t securityStatus{0};
 
   double latencyAvg100{0}, latencyAvg1000{0}, latencyAvg10000{0}, latencyAvg1000000{0};
@@ -297,7 +300,10 @@ struct DNSDistStats
     {"fd-usage", getOpenFileDescriptors},
     {"dyn-blocked", &dynBlocked},
     {"dyn-block-nmg-size", [](const std::string&) { return g_dynblockNMG.getLocal()->size(); }},
-    {"security-status", &securityStatus}
+    {"security-status", &securityStatus},
+    // Latency histogram
+    {"latency-sum", &latencySum},
+    {"latency-count", getLatencyCount},
   };
 };
 
@@ -389,6 +395,9 @@ struct MetricDefinitionStorage {
     { "dyn-blocked",            MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because of a dynamic block")},
     { "dyn-block-nmg-size",     MetricDefinition(PrometheusMetricType::gauge,   "Number of dynamic blocks entries") },
     { "security-status",        MetricDefinition(PrometheusMetricType::gauge,   "Security status of this software. 0=unknown, 1=OK, 2=upgrade recommended, 3=upgrade mandatory") },
+    // Latency histogram
+    { "latency-sum",            MetricDefinition(PrometheusMetricType::counter, "Total response time in milliseconds")},
+    { "latency-count",          MetricDefinition(PrometheusMetricType::counter, "Number of queries contributing to response time histogram")},
   };
 };
 
index a25d8572c058d9b63f2356e0254a5f919ab05ada..4827a6aa0274ce1096aee356b3b25f2ac8cae56c 100644 (file)
@@ -525,6 +525,24 @@ private:
 };
 #endif
 
+class SNIRule : public DNSRule
+{
+public:
+  SNIRule(const std::string& name) : d_sni(name)
+  {
+  }
+  bool matches(const DNSQuestion* dq) const override
+  {
+    return dq->sni == d_sni;
+  }
+  string toString() const override
+  {
+    return "SNI == " + d_sni;
+  }
+private:
+  std::string d_sni;
+};
+
 class SuffixMatchNodeRule : public DNSRule
 {
 public:
index 884a5ad2217c1fa1579fd42429b3a1992f0a0195..c07abb5be0b03da8f2ca88601d899d9d5018c540 100644 (file)
@@ -1,6 +1,43 @@
 Changelog
 =========
 
+.. changelog::
+  :version: 1.4.0-beta1
+  :released: 6th of June 2019
+
+    .. change::
+    :tags: Bug Fixes, DoH
+    :pullreq: 7814
+    :tickets: 7810
+
+    DoH: Don't let 'self' dangling while parsing the request's qname, this could lead to a crash
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 7823
+
+    Fix minor issues reported by Coverity
+
+  .. change::
+    :tags: New Features, DoT, DoH
+    :pullreq: 7825
+    :tickets: 7210
+
+    Implement SNIRule for DoT and DoH
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 7833
+
+    Remove second, incomplete copy of lua EDNSOptionCode table
+
+  .. change::
+    :tags: Improvements, Prometheus
+    :pullreq: 7853
+    :tickets: 6088
+
+    Support Prometheus latency histograms (Marlin Cremers)
+
 .. changelog::
   :version: 1.4.0-alpha2
   :released: 26th of April 2019
index 1bbf2431f145ea1acd4c4651f6353b7fd761b5ee..623ef6c2d4ac4ea223344a0138c0e08406841ef8 100644 (file)
@@ -391,7 +391,7 @@ Servers
       setCD=BOOL,            -- Set the CD (Checking Disabled) flag in the health-check query, default: false
       maxCheckFailures=NUM,  -- Allow NUM check failures before declaring the backend down, default: 1
       checkInterval=NUM      -- The time in seconds between health checks
-      mustResolve=BOOL,      -- Set to true when the health check MUST return a NOERROR RCODE and an answer
+      mustResolve=BOOL,      -- Set to true when the health check MUST return a RCODE different from NXDomain, ServFail and Refused. Default is false, meaning that every RCODE except ServFail is considered valid
       useClientSubnet=BOOL,  -- Add the client's IP address in the EDNS Client Subnet option when forwarding the query to this backend
       source=STRING,         -- The source address or interface to use for queries to this backend, by default this is left to the kernel's address selection
                              -- The following formats are supported:
@@ -581,7 +581,7 @@ Pools are automatically created when a server is added to a pool (with :func:`ne
 PacketCache
 ~~~~~~~~~~~
 
-A Pool can have a packet cache to answer queries directly in stead of going to the backend.
+A Pool can have a packet cache to answer queries directly instead of going to the backend.
 See :doc:`../guides/cache` for a how to.
 
 .. function:: newPacketCache(maxEntries[, maxTTL=86400[, minTTL=0[, temporaryFailureTTL=60[, staleTTL=60[, dontAge=false[, numberOfShards=1[, deferrableInsertLock=true[, maxNegativeTTL=3600[, parseECS=false]]]]]]]) -> PacketCache
@@ -1051,6 +1051,35 @@ faster than the existing rules.
 
     Return a string describing the rules and range exclusions of this DynBlockRulesGroup.
 
+SuffixMatchNode
+~~~~~~~~~~~~~~~
+
+A SuffixMatchNode can be used to quickly check whether a given name belongs to a set or not. This is achieved
+using an efficient tree structure based on DNS labels, making lookups cheap.
+Be careful that Suffix Node matching will match for any sub-domain, regardless of the depth, under the name added to the set. For example,
+if 'example.com.' is added to the set, 'www.example.com.' and 'sub.www.example.com.' will match as well.
+If you are looking for exact name matching, your might want to consider using a :class:`DNSNameSet` instead.
+
+.. function:: newSuffixMatchNode()
+
+  Creates a new :class:`SuffixMatchNode`.
+
+.. class:: SuffixMatchNode
+
+  Represent a set of DNS suffixes for quick matching.
+
+  .. method:: SuffixMatchNode:add(name)
+
+    Add a suffix to the current set.
+
+    :param DNSName name: The suffix to add to the set.
+
+  .. method:: SuffixMatchNode:check(name) -> bool
+
+    Return true if the given name is a sub-domain of one of those in the set, and false otherwise.
+
+    :param DNSName name: The name to test against the set.
+
 Other functions
 ---------------
 
index e451eb62e06616619ff862d6cd75970def55b775..c19d6dad9bfbbbb0d46e3eec1a2aa5e6367a2ee5 100644 (file)
@@ -92,6 +92,15 @@ This state can be modified from the various hooks.
 
     :returns: A table of EDNSOptionView objects, indexed on the ECS Option code
 
+  .. method:: DNSQuestion:getServerNameIndication() -> string
+
+    .. versionadded:: 1.4.0
+
+    Return the TLS Server Name Indication (SNI) value sent by the client over DoT or DoH, if any. See :func:`SNIRule`
+    for more information, especially about the availability of SNI over DoH.
+
+    :returns: A string containing the TLS SNI value, if any
+
   .. method:: DNSQuestion:getTag(key) -> string
 
     .. versionadded:: 1.2.0
index a0d1e38b51b67ef98b284022763db2243d3c8108..73398baa673a4c7818ef44ab2f06e1736e654227 100644 (file)
@@ -521,16 +521,6 @@ Functions for manipulating Self-Answered Response Rules:
 
   Move the last self answered response rule to the first position.
 
-Function for pool related rules
-
-.. function:: PoolAvailableRule(poolname)
-
-  .. versionadded:: 1.3.3
-
-  Check whether a pool has any servers available to handle queries
-
-  :param string poolname: Pool to check
-
 .. _RulesIntro:
 
 Matching Packets (Selectors)
@@ -585,6 +575,21 @@ These ``DNSRule``\ s be one of the following items:
 
   :param int rcode: The RCODE to match on
 
+.. function:: HTTPHeaderRule(name, regex)
+  .. versionadded:: 1.4.0
+
+  Matches DNS over HTTPS queries with a HTTP header ``name`` whose content matches the regular expression ``regex``.
+
+  :param str name: The case-insensitive name of the HTTP header to match on
+  :param str regex: A regular expression to match the content of the specified header
+
+.. function:: HTTPPathRule(path)
+  .. versionadded:: 1.4.0
+
+  Matches DNS over HTTPS queries with a HTTP path of ``path``. For example, if the query has been sent to the https://192.0.2.1:443/PowerDNS?dns=... URL, the path would be '/PowerDNS'.
+
+  :param str path: The exact HTTP path to match on
+
 .. function:: MaxQPSIPRule(qps[, v4Mask[, v6Mask[, burst[, expiration[, cleanupDelay[, scanFraction]]]]]])
   .. versionchanged:: 1.3.1
     Added the optional parameters ``expiration``, ``cleanupDelay`` and ``scanFraction``.
@@ -636,7 +641,7 @@ These ``DNSRule``\ s be one of the following items:
 .. function:: QClassRule(qclass)
 
   Matches queries with the specified ``qclass``.
-  ``class`` can be specified as an integer or as one of the built-in :ref:`DNSQClass`.
+  ``class`` can be specified as an integer or as one of the built-in :ref:`DNSClass`.
 
   :param int qclass: The Query Class to match on
 
@@ -649,7 +654,10 @@ These ``DNSRule``\ s be one of the following items:
    :param string qname: Qname to match
 
 .. function:: QNameSetRule(set)
-  Matches if the set contains exact qname.
+
+  .. versionadded:: 1.4.0
+
+   Matches if the set contains exact qname.
 
    To match subdomain names, see :func:`SuffixMatchNodeRule`.
 
@@ -738,6 +746,17 @@ These ``DNSRule``\ s be one of the following items:
 
   :param str regex: The regular expression to match the QNAME.
 
+.. function:: SNIRule(name)
+  .. versionadded:: 1.4.0
+
+  Matches against the TLS Server Name Indication value sent by the client, if any. Only makes
+  sense for DoT or DoH, and for that last one matching on the HTTP Host header using :func:`HTTPHeaderRule`
+  might provide more consistent results.
+  As of the version 2.3.0-beta of h2o, it is unfortunately not possible to extract the SNI value from DoH
+  connections, and it is therefore necessary to use the HTTP Host header until version 2.3.0 is released.
+
+  :param str name: The exact SNI name to match.
+
 .. function:: SuffixMatchNodeRule(smn[, quiet])
 
   Matches based on a group of domain suffixes for rapid testing of membership.
@@ -767,6 +786,21 @@ These ``DNSRule``\ s be one of the following items:
 
   Matches if the query has trailing data.
 
+.. function:: PoolAvailableRule(poolname)
+
+  .. versionadded:: 1.3.3
+
+  Check whether a pool has any servers available to handle queries
+
+  .. code-block:: Lua
+
+    --- Send queries to default pool when servers are available
+    addAction(PoolAvailableRule(""), PoolAction(""))
+    --- Send queries to fallback pool if not
+    addAction(AllRule(), PoolAction("fallback"))
+
+  :param string poolname: Pool to check
+
 Combining Rules
 ~~~~~~~~~~~~~~~
 
@@ -817,21 +851,21 @@ The following actions exist.
 .. function:: DelayAction(milliseconds)
 
   Delay the response by the specified amount of milliseconds (UDP-only).
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
   :param int milliseconds: The amount of milliseconds to delay the response
 
 .. function:: DelayResponseAction(milliseconds)
 
   Delay the response by the specified amount of milliseconds (UDP-only).
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
   :param int milliseconds: The amount of milliseconds to delay the response
 
 .. function:: DisableECSAction()
 
   Disable the sending of ECS to the backend.
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
 .. function:: DisableValidationAction()
 
@@ -870,14 +904,14 @@ The following actions exist.
 .. function:: ECSOverrideAction(override)
 
   Whether an existing EDNS Client Subnet value should be overridden (true) or not (false).
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
   :param bool override: Whether or not to override ECS value
 
 .. function:: ECSPrefixLengthAction(v4, v6)
 
   Set the ECS prefix length.
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
   :param int v4: The IPv4 netmask length
   :param int v6: The IPv6 netmask length
@@ -898,7 +932,7 @@ The following actions exist.
   When logging to a file, the ``binary`` optional parameter specifies whether we log in binary form (default) or in textual form.
   The ``append`` optional parameter specifies whether we open the file for appending or truncate each time (default).
   The ``buffered`` optional parameter specifies whether writes to the file are buffered (default) or not.
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
   :param string filename: File to log to. Set to an empty string to log to the normal stdout log, this only works when ``-v`` is set on the command line.
   :param bool binary: Do binary logging. Default true
@@ -925,19 +959,19 @@ The following actions exist.
 
   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 rule.
+  Subsequent rules are processed after this action.
 
   :param int option: The EDNS0 option number
 
 .. function:: NoneAction()
 
   Does nothing.
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
 .. function:: NoRecurseAction()
 
   Strip RD bit from the question, let it go through.
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
 .. function:: PoolAction(poolname)
 
@@ -1018,7 +1052,7 @@ The following actions exist.
   If both IPv4 and IPv6 masks are supplied the IPv4 one will be used for IPv4 clients
   and the IPv6 one for IPv6 clients. Otherwise the first mask is used for both, and
   can actually be an IPv6 mask.
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
   :param string v4: The IPv4 netmask, for example "192.0.2.1/32"
   :param string v6: The IPv6 netmask, if any
@@ -1030,14 +1064,14 @@ The following actions exist.
 .. function:: SNMPTrapAction([message])
 
   Send an SNMP trap, adding the optional ``message`` string as the query description.
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
   :param string message: The message to include
 
 .. function:: SNMPTrapResponseAction([message])
 
   Send an SNMP trap, adding the optional ``message`` string as the query description.
-  Subsequent rules are processed after this rule.
+  Subsequent rules are processed after this action.
 
   :param string message: The message to include
 
index 10f7de4ff1ba49c53c23e7887f9bf64228a9cdc5..ee806bb425c2390d558452ecf04011d2dcfbe9a4 100644 (file)
@@ -99,6 +99,19 @@ latency-slow
 ------------
 Number of queries answered in more than 1 second.
 
+latency-sum
+-----------
+Total response time of all queries combined in milliseconds since the start of dnsdist. Can be used to calculate the
+average response time over all queries.
+
+latency-count
+-------------
+Number of queries contributing to response time histogram
+
+latency-bucket
+--------------
+Number of queries contributing to response time histogram per latency bucket
+
 latency0-1
 ----------
 Number of queries answered in less than 1 ms.
index 9b183e5f151470f8b979f7d0f72a9321232c89eb..4e585d4afa58c96b7c2ef445bf6368a8cd8bdc9f 100644 (file)
@@ -185,6 +185,13 @@ static int processDOHQuery(DOHUnit* du)
     dq.ednsAdded = du->ednsAdded;
     dq.du = du;
     queryId = ntohs(dh->id);
+#ifdef HAVE_H2O_SOCKET_GET_SSL_SERVER_NAME
+    h2o_socket_t* sock = du->req->conn->callbacks->get_socket(du->req->conn);
+    const char * sni = h2o_socket_get_ssl_server_name(sock);
+    if (sni != nullptr) {
+      dq.sni = sni;
+    }
+#endif /* HAVE_H2O_SOCKET_BET_SSL_SERVER_NAME */
 
     std::shared_ptr<DownstreamState> ss{nullptr};
     auto result = processQuery(dq, cs, holders, ss);
@@ -279,16 +286,17 @@ static void on_generator_dispose(void *_self)
 static void doh_dispatch_query(DOHServerConfig* dsc, h2o_handler_t* self, h2o_req_t* req, std::string&& query, const ComboAddress& local, const ComboAddress& remote)
 {
   try {
-    auto du = std::unique_ptr<DOHUnit>(new DOHUnit);
-    du->self = reinterpret_cast<DOHUnit**>(h2o_mem_alloc_shared(&req->pool, sizeof(*self), on_generator_dispose));
     uint16_t qtype;
     DNSName qname(query.c_str(), query.size(), sizeof(dnsheader), false, &qtype);
+
+    auto du = std::unique_ptr<DOHUnit>(new DOHUnit);
     du->req = req;
-    du->query = std::move(query);
     du->dest = local;
     du->remote = remote;
     du->rsock = dsc->dohresponsepair[0];
+    du->query = std::move(query);
     du->qtype = qtype;
+    du->self = reinterpret_cast<DOHUnit**>(h2o_mem_alloc_shared(&req->pool, sizeof(*self), on_generator_dispose));
     auto ptr = du.release();
     *(ptr->self) = ptr;
     try  {
index 0114743a5fc0c851bf6b7f024e22d48b563aa8dd..684a276c314b05de11411c6dd78ddba9ae16f575 100644 (file)
@@ -54,29 +54,23 @@ static void openssl_thread_cleanup()
   OPENSSL_free(openssllocks);
 }
 
-#else
-static void openssl_thread_setup()
-{
-}
-
-static void openssl_thread_cleanup()
-{
-}
-#endif /* (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER) */
-
 static std::atomic<uint64_t> s_users;
+#endif /* (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER) */
 
 void registerOpenSSLUser()
 {
+#if (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER)
   if (s_users.fetch_add(1) == 0) {
     SSL_load_error_strings();
     OpenSSL_add_ssl_algorithms();
     openssl_thread_setup();
   }
+#endif
 }
 
 void unregisterOpenSSLUser()
 {
+#if (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER)
   if (s_users.fetch_sub(1) == 1) {
     ERR_free_strings();
 
@@ -89,6 +83,7 @@ void unregisterOpenSSLUser()
     CRYPTO_cleanup_all_ex_data();
     openssl_thread_cleanup();
   }
+#endif
 }
 
 #endif /* HAVE_LIBSSL */
index ffe066b72fdf6a92462ab90b3b02bee3a3575829..00781ce32b8430f4473f5bd01a0157758a56b394 100644 (file)
@@ -3,6 +3,19 @@ AC_DEFUN([PDNS_CHECK_LIBH2OEVLOOP], [
   PKG_CHECK_MODULES([LIBH2OEVLOOP], [libh2o-evloop], [
     [HAVE_LIBH2OEVLOOP=1]
     AC_DEFINE([HAVE_LIBH2OEVLOOP], [1], [Define to 1 if you have libh2o-evloop])
+    save_CFLAGS=$CFLAGS
+    save_LIBS=$LIBS
+    CFLAGS="$LIBH2OEVLOOP_CFLAGS $CFLAGS"
+    LIBS="$LIBH2OEVLOOP_LIBS $LIBS"
+    AC_CHECK_DECLS([h2o_socket_get_ssl_server_name], [
+          AC_DEFINE([HAVE_H2O_SOCKET_GET_SSL_SERVER_NAME], [1], [define to 1 if h2o_socket_get_ssl_server_name is available.])
+        ],
+        [ : ],
+        [AC_INCLUDES_DEFAULT
+          #include <h2o/socket.h>
+      ])
+    CFLAGS=$save_CFLAGS
+    LIBS=$save_LIBS
   ], [ : ])
   AM_CONDITIONAL([HAVE_LIBH2OEVLOOP], [test "x$LIBH2OEVLOOP_LIBS" != "x"])
 ])
index 6e77c7840aa30b78354f8c1e190798f1e98a6a07..959648134f2d7a3f201485ad1cfba86fbde0b825 100644 (file)
@@ -352,6 +352,7 @@ public:
 
     return got;
   }
+
   void close() override
   {
     if (d_conn) {
@@ -359,6 +360,17 @@ public:
     }
   }
 
+  std::string getServerNameIndication() override
+  {
+    if (d_conn) {
+      const char* value = SSL_get_servername(d_conn.get(), TLSEXT_NAMETYPE_host_name);
+      if (value) {
+        return std::string(value);
+      }
+    }
+    return std::string();
+  }
+
 private:
   std::unique_ptr<SSL, void(*)(SSL*)> d_conn;
   unsigned int d_timeout;
@@ -860,6 +872,23 @@ public:
     return got;
   }
 
+  std::string getServerNameIndication() override
+  {
+    if (d_conn) {
+      unsigned int type;
+      size_t name_len = 256;
+      std::string sni;
+      sni.resize(name_len);
+
+      int res = gnutls_server_name_get(d_conn.get(), const_cast<char*>(sni.c_str()), &name_len, &type, 0);
+      if (res == GNUTLS_E_SUCCESS) {
+        sni.resize(name_len);
+        return sni;
+      }
+    }
+    return std::string();
+  }
+
   void close() override
   {
     if (d_conn) {
index c7f3070338e29e4c15c1ef05d37cebb43964cbfd..ac752f506fc7439a002a2c480b2e03780e72b8e4 100644 (file)
@@ -340,10 +340,8 @@ void DNSPacket::wrapup()
         pos->dr.d_content->toPacket(pw);
         if(pw.size() + optsize > (d_tcp ? 65535 : getMaxReplyLen())) {
           pw.rollback();
-          if(pos->dr.d_place == DNSResourceRecord::ANSWER || pos->dr.d_place == DNSResourceRecord::AUTHORITY) {
-            pw.truncate();
-            pw.getHeader()->tc=1;
-          }
+          pw.truncate();
+          pw.getHeader()->tc=1;
           goto noCommit;
         }
       }
index 8f382b02ddfb128365dd8f0af07126654e71a081..505c2812f59f277baadc3c0a1a87db8b38800087 100644 (file)
@@ -147,7 +147,7 @@ public:
   ComboAddress d_remote; //28
   TSIGHashEnum d_tsig_algo{TSIG_MD5}; //4
 
-  int d_ednsRawPacketSizeLimit; // only used for Lua record
+  int d_ednsRawPacketSizeLimit{-1}; // only used for Lua record
   uint16_t qclass{QClass::IN};  //!< class of the question - should always be INternet 2
   QType qtype;  //!< type of the question 2
 
index 233258a3efb57fcafb3a448638013ec11c03b4dc..f89ff3f67a003b77c016000620c4aab5b4bcb9fb 100644 (file)
@@ -484,6 +484,11 @@ string PacketReader::getUnquotedText(bool lenField)
   else
     stop_at = d_recordlen;
 
+  /* think unsigned overflow */
+  if (stop_at < d_pos) {
+    throw std::out_of_range("getUnquotedText out of record range");
+  }
+
   if(stop_at == d_pos)
     return "";
 
index 44a720b35408bf878e18a7a06443b2818a7c0702..3924a698d17a05007215ac8d4137dd765dedf65a 100644 (file)
@@ -133,7 +133,7 @@ try
       contentCode=ntohs(d_lcc->lcc_protocol);
     }
 
-    if(contentCode==0x0800 && d_ip->ip_p==17) { // udp
+    if(contentCode==0x0800 && (d_pheader.caplen >= (d_skipMediaHeader + sizeof(*d_ip))) && d_ip->ip_p==17) { // udp
       if (d_pheader.caplen < (d_skipMediaHeader + (4 * d_ip->ip_hl) + sizeof(*d_udp))) {
         d_runts++;
         continue;
@@ -152,7 +152,7 @@ try
       d_correctpackets++;
       return true;
     }
-    else if(contentCode==0x86dd && d_ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt==17) { // udpv6, we ignore anything with extension hdr
+    else if(contentCode==0x86dd && (d_pheader.caplen >= (d_skipMediaHeader + sizeof(*d_ip6))) && d_ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt==17) { // udpv6, we ignore anything with extension hdr
       if (d_pheader.caplen < (d_skipMediaHeader + sizeof(struct ip6_hdr) + sizeof(struct udphdr))) {
         d_runts++;
         continue;
index d89419e23229f5bb130a627b32975658e89abe13..d1d491d0b1ace121edd7f7ffab801b3a10f55a9b 100644 (file)
@@ -397,6 +397,7 @@ CDNSKEYRecordContent::CDNSKEYRecordContent() {}
 boilerplate_conv(RKEY, 57, 
                  conv.xfr16BitInt(d_flags); 
                  conv.xfr8BitInt(d_protocol); 
+                 conv.xfr8BitInt(d_algorithm); 
                  conv.xfrBlob(d_key);
                  )
 RKEYRecordContent::RKEYRecordContent() {}
index 69dde0430eb32e02e4d57c7091ff193e1c5099d8..c0d5526f4f2f7ef5b76b6a7377aed5acdddbe74d 100644 (file)
@@ -303,3 +303,4 @@ uint32_t calculateEditSOA(uint32_t old_serial, const string& kind, const DNSName
 // for SOA-EDIT-DNSUPDATE/API
 bool increaseSOARecord(DNSResourceRecord& dr, const string& increaseKind, const string& editKind);
 bool makeIncreasedSOARecord(SOAData& sd, const string& increaseKind, const string& editKind, DNSResourceRecord& rrout);
+DNSZoneRecord makeEditedDNSZRFromSOAData(DNSSECKeeper& dk, const SOAData& sd, DNSResourceRecord::Place place=DNSResourceRecord::ANSWER);
index b98b21672d426b437d18d5261ef0746ca86d60af..f52ce2912b12f378139a97d92ec55fbdaed0d0bb 100644 (file)
@@ -14,26 +14,26 @@ DnstapMessage::DnstapMessage(const std::string& identity, const ComboAddress* re
   dnstap::Message* message = proto_message.mutable_message();
 
   message->set_type(!dh->qr ? dnstap::Message_Type_CLIENT_QUERY : dnstap::Message_Type_CLIENT_RESPONSE);
-
-  message->set_socket_family(requestor->sin4.sin_family == AF_INET ? dnstap::INET : dnstap::INET6);
   message->set_socket_protocol(isTCP ? dnstap::TCP : dnstap::UDP);
 
-  if (requestor->sin4.sin_family == AF_INET) {
-    message->set_query_address(&requestor->sin4.sin_addr.s_addr, sizeof(requestor->sin4.sin_addr.s_addr));
-  }
-  else if (requestor->sin4.sin_family == AF_INET6) {
-    message->set_query_address(&requestor->sin6.sin6_addr.s6_addr, sizeof(requestor->sin6.sin6_addr.s6_addr));
+  if (requestor != nullptr) {
+    message->set_socket_family(requestor->sin4.sin_family == AF_INET ? dnstap::INET : dnstap::INET6);
+    if (requestor->sin4.sin_family == AF_INET) {
+      message->set_query_address(&requestor->sin4.sin_addr.s_addr, sizeof(requestor->sin4.sin_addr.s_addr));
+    } else if (requestor->sin4.sin_family == AF_INET6) {
+      message->set_query_address(&requestor->sin6.sin6_addr.s6_addr, sizeof(requestor->sin6.sin6_addr.s6_addr));
+    }
+    message->set_query_port(ntohs(requestor->sin4.sin_port));
   }
-  message->set_query_port(ntohs(requestor->sin4.sin_port));
-
-  if (requestor->sin4.sin_family == AF_INET) {
-    message->set_response_address(&responder->sin4.sin_addr.s_addr, sizeof(responder->sin4.sin_addr.s_addr));
+  if (responder != nullptr) {
+    message->set_socket_family(responder->sin4.sin_family == AF_INET ? dnstap::INET : dnstap::INET6);
+    if (responder->sin4.sin_family == AF_INET) {
+      message->set_response_address(&responder->sin4.sin_addr.s_addr, sizeof(responder->sin4.sin_addr.s_addr));
+    } else if (responder->sin4.sin_family == AF_INET6) {
+      message->set_response_address(&responder->sin6.sin6_addr.s6_addr, sizeof(responder->sin6.sin6_addr.s6_addr));
+    }
+    message->set_response_port(ntohs(responder->sin4.sin_port));
   }
-  else if (requestor->sin4.sin_family == AF_INET6) {
-    message->set_response_address(&responder->sin6.sin6_addr.s6_addr, sizeof(responder->sin6.sin6_addr.s6_addr));
-  }
-  message->set_response_port(ntohs(responder->sin4.sin_port));
-
   if (queryTime != nullptr) {
     message->set_query_time_sec(queryTime->tv_sec);
     message->set_query_time_nsec(queryTime->tv_nsec);
index 5f326ede126fd840935e9024b38d21048962bcbd..d69b6c6866c030de969fb679203031ca03d7598c 100644 (file)
@@ -62,7 +62,7 @@ unsigned int makeUsec(const struct timeval& tv)
 struct BenchQuery
 {
   BenchQuery(const std::string& qname_, uint16_t qtype_) : qname(qname_), qtype(qtype_), udpUsec(0), tcpUsec(0), answerSecond(0) {}
-  BenchQuery(){}
+  BenchQuery(): qtype(0), udpUsec(0), tcpUsec(0), answerSecond(0) {}
   DNSName qname;
   uint16_t qtype;
   uint32_t udpUsec, tcpUsec;
@@ -83,7 +83,7 @@ try
   if(!g_onlyTCP) {
     Socket udpsock(g_dest.sin4.sin_family, SOCK_DGRAM);
     
-    udpsock.sendTo(string((char*)&*packet.begin(), (char*)&*packet.end()), g_dest);
+    udpsock.sendTo(string(packet.begin(), packet.end()), g_dest);
     ComboAddress origin;
     res = waitForData(udpsock.getHandle(), 0, 1000 * g_timeoutMsec);
     if(res < 0)
@@ -116,7 +116,7 @@ try
   sock.connect(g_dest);
   uint16_t len = htons(packet.size());
   string tcppacket((char*)& len, 2);
-  tcppacket.append((char*)&*packet.begin(), (char*)&*packet.end());
+  tcppacket.append(packet.begin(), packet.end());
 
   sock.writen(tcppacket);
 
index 354eba7461bb2172d0f6d587e8ea1d2108c8429c..5d457dc47f6f0888fb416571ca5eb333967c1b30 100644 (file)
@@ -202,12 +202,12 @@ try
   auto dh = reinterpret_cast<struct dnsheader*>(buffer);
 
   for(;;) {
-    uint16_t len = recvfrom(s.getHandle(), buffer, sizeof(buffer), 0, reinterpret_cast<struct sockaddr*>(&rem), &socklen);
+    ssize_t len = recvfrom(s.getHandle(), buffer, sizeof(buffer), 0, reinterpret_cast<struct sockaddr*>(&rem), &socklen);
 
     if(len < 0)
       unixDie("recvfrom");
 
-    if (len < sizeof(dnsheader))
+    if (static_cast<size_t>(len) < sizeof(dnsheader))
       unixDie("too small " + std::to_string(len));
 
     if(dh->qr)
index c592b7cc31ae31a59035a3eee29e15d87e48287e..c3a1dc7e3e34245c81b98a7a1de4e0d9709b10d9 100644 (file)
@@ -221,7 +221,7 @@ string DynListener::getLine()
   vector<char> mesg;
   mesg.resize(1024000);
 
-  int len;
+  ssize_t len;
 
   ComboAddress remote;
   socklen_t remlen=remote.getSocklen();
@@ -284,12 +284,12 @@ string DynListener::getLine()
     else if(len==0)
       throw PDNSException("Guardian exited - going down as well");
 
-    if(len == (int)mesg.size())
+    if(static_cast<size_t>(len) == mesg.size())
       throw PDNSException("Line on control console was too long");
 
     mesg[len]=0;
   }
-  
+
   return &mesg[0];
 }
 
index 617aa73d5cfd76df42c687f3c7d52b2500c39e0a..62ccd72ebd10a5b3f52c9b5ebd7b3ef89c66f6d9 100644 (file)
@@ -3,13 +3,19 @@
 
 #include "config.h"
 #include "fstrm_logger.hh"
+
+#ifdef RECURSOR
+#include "logger.hh"
+#else
 #include "dolog.hh"
+#endif
 
 #define DNSTAP_CONTENT_TYPE            "protobuf:dnstap.Dnstap"
 
 #ifdef HAVE_FSTRM
 
-FrameStreamLogger::FrameStreamLogger(const int family, const std::string& address, bool connect): d_family(family), d_address(address)
+FrameStreamLogger::FrameStreamLogger(const int family, const std::string& address, bool connect,
+    const std::unordered_map<string,unsigned>& options): d_family(family), d_address(address)
 {
   fstrm_res res;
 
@@ -78,6 +84,44 @@ FrameStreamLogger::FrameStreamLogger(const int family, const std::string& addres
       throw std::runtime_error("FrameStreamLogger: fstrm_iothr_options_set_queue_model failed: " + std::to_string(res));
     }
 
+    if (options.find("bufferHint") != options.end() && options.at("bufferHint")) {
+      res = fstrm_iothr_options_set_buffer_hint(d_iothropt, options.at("bufferHint"));
+      if (res != fstrm_res_success) {
+        throw std::runtime_error("FrameStreamLogger: fstrm_iothr_options_set_buffer_hint failed: " + std::to_string(res));
+      }
+    }
+    if (options.find("flushTimeout") != options.end() && options.at("flushTimeout")) {
+      res = fstrm_iothr_options_set_flush_timeout(d_iothropt, options.at("flushTimeout"));
+      if (res != fstrm_res_success) {
+        throw std::runtime_error("FrameStreamLogger: fstrm_iothr_options_set_flush_timeout failed: " + std::to_string(res));
+      }
+    }
+    if (options.find("inputQueueSize") != options.end() && options.at("inputQueueSize")) {
+      res = fstrm_iothr_options_set_input_queue_size(d_iothropt, options.at("inputQueueSize"));
+      if (res != fstrm_res_success) {
+        throw std::runtime_error("FrameStreamLogger: fstrm_iothr_options_set_input_queue_size failed: " + std::to_string(res));
+      }
+    }
+    if (options.find("outputQueueSize") != options.end() && options.at("outputQueueSize")) {
+      res = fstrm_iothr_options_set_output_queue_size(d_iothropt, options.at("outputQueueSize"));
+      if (res != fstrm_res_success) {
+        throw std::runtime_error("FrameStreamLogger: fstrm_iothr_options_set_output_queue_size failed: " + std::to_string(res));
+      }
+    }
+    if (options.find("queueNotifyThreshold") != options.end() && options.at("queueNotifyThreshold")) {
+      res = fstrm_iothr_options_set_queue_notify_threshold(d_iothropt, options.at("queueNotifyThreshold"));
+      if (res != fstrm_res_success) {
+        throw std::runtime_error("FrameStreamLogger: fstrm_iothr_options_set_queue_notify_threshold failed: " + std::to_string(res));
+      }
+    }
+    if (options.find("setReopenInterval") != options.end() && options.at("setReopenInterval")) {
+      res = fstrm_iothr_options_set_reopen_interval(d_iothropt, options.at("setReopenInterval"));
+      if (res != fstrm_res_success) {
+        throw std::runtime_error("FrameStreamLogger: fstrm_iothr_options_set_reopen_interval failed: " + std::to_string(res));
+      }
+    }
+
+
     if (connect) {
       d_iothr = fstrm_iothr_init(d_iothropt, &d_writer);
       if (!d_iothr) {
@@ -137,7 +181,11 @@ void FrameStreamLogger::queueData(const std::string& data)
   }
   uint8_t *frame = (uint8_t*)malloc(data.length());
   if (!frame) {
+#ifdef RECURSOR
+    g_log<<Logger::Warning<<"FrameStreamLogger: cannot allocate memory for stream."<<std::endl;
+#else
     warnlog("FrameStreamLogger: cannot allocate memory for stream.");
+#endif
     return;
   }
   memcpy(frame, data.c_str(), data.length());
@@ -149,11 +197,19 @@ void FrameStreamLogger::queueData(const std::string& data)
     // Frame successfully queued.
   } else if (res == fstrm_res_again) {
     free(frame);
+#ifdef RECURSOR
+    g_log<<Logger::Warning<<"FrameStreamLogger: queue full, dropping."<<std::endl;
+#else
     warnlog("FrameStreamLogger: queue full, dropping.");
-  } else {
+#endif
+ } else {
     // Permanent failure.
     free(frame);
+#ifdef RECURSOR
+    g_log<<Logger::Warning<<"FrameStreamLogger: submitting to queue failed."<<std::endl;
+#else
     warnlog("FrameStreamLogger: submitting to queue failed.");
+#endif
   }
 }
 
index 240b3d6adde12ce807df71f0723f2bf1e818da25..57ee93aed81b0d09584c28777d4c69c5f47bfd8d 100644 (file)
@@ -25,6 +25,7 @@
 
 #ifdef HAVE_FSTRM
 
+#include <unordered_map>
 #include <fstrm.h>
 #include <fstrm/iothr.h>
 #include <fstrm/unix_writer.h>
 class FrameStreamLogger : public RemoteLoggerInterface, boost::noncopyable
 {
 public:
-  FrameStreamLogger(int family, const std::string& address, bool connect);
+  FrameStreamLogger(int family, const std::string& address, bool connect, const std::unordered_map<string,unsigned>& options = std::unordered_map<string,unsigned>());
   virtual ~FrameStreamLogger();
   virtual void queueData(const std::string& data) override;
   virtual std::string toString() const override
   {
     return "FrameStreamLogger to " + d_address;
   }
+  bool logQueries(void) const { return d_logQueries; }
+  bool logResponses(void) const { return d_logResponses; }
+  void setLogQueries(bool flag) { d_logQueries = flag; }
+  void setLogResponses(bool flag) { d_logResponses = flag; }
+
 private:
+
   const int d_family;
   const std::string d_address;
   struct fstrm_iothr_queue *d_ioqueue{nullptr};
@@ -56,6 +63,11 @@ private:
   struct fstrm_iothr *d_iothr{nullptr};
 
   void cleanup();
+
+  bool d_logQueries{true};
+  bool d_logResponses{true};
 };
 
+#else
+class FrameStreamLogger : public RemoteLoggerInterface, boost::noncopyable {};
 #endif /* HAVE_FSTRM */
index 474b8b484b473a4e89c54767a9ad677e2f2d0f80..f58d0e31b2770cb2260ba261b1d4c2cb89580004 100644 (file)
@@ -290,14 +290,8 @@ void ComboAddress::truncate(unsigned int bits) noexcept
   *place &= (~((1<<bitsleft)-1));
 }
 
-size_t sendMsgWithTimeout(int fd, const char* buffer, size_t len, int idleTimeout, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int totalTimeout, int flags)
+size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
 {
-  int remainingTime = totalTimeout;
-  time_t start = 0;
-  if (totalTimeout) {
-    start = time(nullptr);
-  }
-
   struct msghdr msgh;
   struct iovec iov;
   char cbuf[256];
@@ -349,10 +343,14 @@ size_t sendMsgWithTimeout(int fd, const char* buffer, size_t len, int idleTimeou
       }
 
       /* partial write */
+      firstTry = false;
       iov.iov_len -= written;
       iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
       written = 0;
     }
+    else if (res == 0) {
+      return res;
+    }
     else if (res == -1) {
       if (errno == EINTR) {
         continue;
@@ -360,150 +358,20 @@ size_t sendMsgWithTimeout(int fd, const char* buffer, size_t len, int idleTimeou
       else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS || errno == ENOTCONN) {
         /* EINPROGRESS might happen with non blocking socket,
            especially with TCP Fast Open */
-        if (totalTimeout <= 0 && idleTimeout <= 0) {
-          return sent;
-        }
-
-        if (firstTry) {
-          int res = waitForRWData(fd, false, (totalTimeout == 0 || idleTimeout <= remainingTime) ? idleTimeout : remainingTime, 0);
-          if (res > 0) {
-            /* there is room available */
-            firstTry = false;
-          }
-          else if (res == 0) {
-            throw runtime_error("Timeout while waiting to write data");
-          } else {
-            throw runtime_error("Error while waiting for room to write data");
-          }
-        }
-        else {
-          throw runtime_error("Timeout while waiting to write data");
-        }
+        return sent;
       }
       else {
         unixDie("failed in sendMsgWithTimeout");
       }
     }
-    if (totalTimeout) {
-      time_t now = time(nullptr);
-      int elapsed = now - start;
-      if (elapsed >= remainingTime) {
-        throw runtime_error("Timeout while sending data");
-      }
-      start = now;
-      remainingTime -= elapsed;
-    }
   }
-  while (firstTry);
+  while (true);
 
   return 0;
 }
 
 template class NetmaskTree<bool>;
 
-bool sendSizeAndMsgWithTimeout(int sock, uint16_t bufferLen, const char* buffer, int idleTimeout, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int totalTimeout, int flags)
-{
-  uint16_t size = htons(bufferLen);
-  char cbuf[256];
-  struct msghdr msgh;
-  struct iovec iov[2];
-  int remainingTime = totalTimeout;
-  time_t start = 0;
-  if (totalTimeout) {
-    start = time(NULL);
-  }
-
-  /* Set up iov and msgh structures. */
-  memset(&msgh, 0, sizeof(struct msghdr));
-  msgh.msg_control = nullptr;
-  msgh.msg_controllen = 0;
-  if (dest) {
-    msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
-    msgh.msg_namelen = dest->getSocklen();
-  }
-  else {
-    msgh.msg_name = nullptr;
-    msgh.msg_namelen = 0;
-  }
-
-  msgh.msg_flags = 0;
-
-  if (localItf != 0 && local) {
-    addCMsgSrcAddr(&msgh, cbuf, local, localItf);
-  }
-
-  iov[0].iov_base = &size;
-  iov[0].iov_len = sizeof(size);
-  iov[1].iov_base = reinterpret_cast<void*>(const_cast<char*>(buffer));
-  iov[1].iov_len = bufferLen;
-
-  size_t pos = 0;
-  size_t sent = 0;
-  size_t nbElements = sizeof(iov)/sizeof(*iov);
-  while (true) {
-    msgh.msg_iov = &iov[pos];
-    msgh.msg_iovlen = nbElements - pos;
-
-    ssize_t res = sendmsg(sock, &msgh, flags);
-    if (res > 0) {
-      size_t written = static_cast<size_t>(res);
-      sent += written;
-
-      if (sent == (sizeof(size) + bufferLen)) {
-        return true;
-      }
-      /* partial write, we need to keep only the (parts of) elements
-         that have not been written.
-      */
-      do {
-        if (written < iov[pos].iov_len) {
-          iov[pos].iov_len -= written;
-          iov[pos].iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov[pos].iov_base) + written);
-          written = 0;
-        }
-        else {
-          written -= iov[pos].iov_len;
-          iov[pos].iov_len = 0;
-          pos++;
-        }
-      }
-      while (written > 0 && pos < nbElements);
-    }
-    else if (res == -1) {
-      if (errno == EINTR) {
-        continue;
-      }
-      else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) {
-        /* EINPROGRESS might happen with non blocking socket,
-           especially with TCP Fast Open */
-        int ret = waitForRWData(sock, false, (totalTimeout == 0 || idleTimeout <= remainingTime) ? idleTimeout : remainingTime, 0);
-        if (ret > 0) {
-          /* there is room available */
-        }
-        else if (ret == 0) {
-          throw runtime_error("Timeout while waiting to send data");
-        } else {
-          throw runtime_error("Error while waiting for room to send data");
-        }
-      }
-      else {
-        unixDie("failed in sendSizeAndMsgWithTimeout");
-      }
-    }
-    if (totalTimeout) {
-      time_t now = time(NULL);
-      int elapsed = now - start;
-      if (elapsed >= remainingTime) {
-        throw runtime_error("Timeout while sending data");
-      }
-      start = now;
-      remainingTime -= elapsed;
-    }
-  }
-
-  return false;
-}
-
 /* requires a non-blocking socket.
    On Linux, we could use MSG_DONTWAIT on a blocking socket
    but this is not portable.
index 457b2b5971e332357275058bb2912d14167e24a0..18d6e881949cd5d23a62bece3e23a4b69ef5018f 100644 (file)
@@ -1059,8 +1059,8 @@ bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destinat
 bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv);
 void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, char* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr);
 ssize_t sendfromto(int sock, const char* data, size_t len, int flags, const ComboAddress& from, const ComboAddress& to);
-size_t sendMsgWithTimeout(int fd, const char* buffer, size_t len, int idleTimeout, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int totalTimeout, int flags);
-bool sendSizeAndMsgWithTimeout(int sock, uint16_t bufferLen, const char* buffer, int idleTimeout, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int totalTimeout, int flags);
+size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags);
+
 /* requires a non-blocking, connected TCP socket */
 bool isTCPSocketUsable(int sock);
 
index e20cfe666be4244db1bc436287e6e5e0deb602e6..cd5f7155c6e4229e031862dec96a0a5bc4e449dd 100644 (file)
@@ -99,8 +99,10 @@ uint32_t getSerialFromRecords(const records_t& records, DNSRecord& soaret)
 
   for(auto iter = found.first; iter != found.second; ++iter) {
     auto soa = std::dynamic_pointer_cast<SOARecordContent>(iter->d_content);
-    soaret = *iter;
-    return soa->d_st.serial;
+    if (soa) {
+      soaret = *iter;
+      return soa->d_st.serial;
+    }
   }
   return 0;
 }
@@ -134,7 +136,9 @@ void writeZoneToDisk(const records_t& records, const DNSName& zone, const std::s
   writeRecords(fp, soarecord);
 
   fclose(fp);
-  rename( (fname+".partial").c_str(), fname.c_str());
+  if (rename( (fname+".partial").c_str(), fname.c_str()) != 0) {
+    throw std::runtime_error("Unable to move the zone file for " + zone.toLogString() + " from " + fname + ".partial to " + fname + ": " + string(strerror(errno)));
+  }
 }
 
 void loadZoneFromDisk(records_t& records, const string& fname, const DNSName& zone)
index 3ebed5dcd35e2034a17a65342877c24254a11a00..0e2b9102b813e491bca4297308dd6cebed8dfd6e 100644 (file)
@@ -465,13 +465,19 @@ static vector<pair<int, ComboAddress> > convWIplist(std::unordered_map<int, wipl
   return ret;
 }
 
+static thread_local unique_ptr<AuthLua4> s_LUA;
+bool g_LuaRecordSharedState;
+
 std::vector<shared_ptr<DNSRecordContent>> luaSynth(const std::string& code, const DNSName& query, const DNSName& zone, int zoneid, const DNSPacket& dnsp, uint16_t qtype)
 {
-  AuthLua4 alua;
+  if(!s_LUA ||                  // we don't have a Lua state yet
+     !g_LuaRecordSharedState) { // or we want a new one even if we had one
+    s_LUA = make_unique<AuthLua4>();
+  }
 
   std::vector<shared_ptr<DNSRecordContent>> ret;
 
-  LuaContext& lua = *alua.getLua();
+  LuaContext& lua = *s_LUA->getLua();
   lua.writeVariable("qname", query);
   lua.writeVariable("who", dnsp.getRemote());
   lua.writeVariable("dh", (dnsheader*)&dnsp.d);
index 794838924ae0106d532b81d3e79d31ed6ff45c26..3d3aee1e053e685b9cb7544929341ff1b1f44558 100644 (file)
 
 #include "uuid-utils.hh"
 
+#ifdef HAVE_FSTRM
+#include "rec-dnstap.hh"
+#include "fstrm_logger.hh"
+bool g_syslog;
+
+static bool isEnabledForQueries(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
+{
+  if (fstreamLoggers == nullptr) {
+    return false;
+  }
+  for (auto& logger : *fstreamLoggers) {
+    if (logger->logQueries()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static void logFstreamQuery(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers, const struct timeval &queryTime, const ComboAddress& ip, bool doTCP, const vector<uint8_t>& packet)
+{
+  if (fstreamLoggers == nullptr)
+    return;
+
+  struct timespec ts;
+  TIMEVAL_TO_TIMESPEC(&queryTime, &ts);
+  RecDnstapMessage message(SyncRes::s_serverID, nullptr, &ip, doTCP, reinterpret_cast<const char*>(&*packet.begin()), packet.size(), &ts, nullptr);
+  std::string str;
+  message.serialize(str);
+
+  for (auto& logger : *fstreamLoggers) {
+    logger->queueData(str);
+  }
+}
+
+static bool isEnabledForResponses(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
+{
+  if (fstreamLoggers == nullptr) {
+    return false;
+  }
+  for (auto& logger : *fstreamLoggers) {
+    if (logger->logResponses()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static void logFstreamResponse(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers, const ComboAddress& ip, bool doTCP, const std::string& packet, const struct timeval& queryTime, const struct timeval& replyTime)
+{
+  if (fstreamLoggers == nullptr)
+    return;
+
+  struct timespec ts1, ts2;
+  TIMEVAL_TO_TIMESPEC(&queryTime, &ts1);
+  TIMEVAL_TO_TIMESPEC(&replyTime, &ts2);
+  RecDnstapMessage message(SyncRes::s_serverID, nullptr, &ip, doTCP, static_cast<const char*>(&*packet.begin()), packet.size(), &ts1, &ts2);
+  std::string str;
+  message.serialize(str);
+
+  for (auto& logger : *fstreamLoggers) {
+    logger->queueData(str);
+  }
+}
+
+#endif // HAVE_FSTRM
+
 static void logOutgoingQuery(const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, boost::optional<const boost::uuids::uuid&> initialRequestId, const boost::uuids::uuid& uuid, const ComboAddress& ip, const DNSName& domain, int type, uint16_t qid, bool doTCP, size_t bytes, boost::optional<Netmask>& srcmask)
 {
   if(!outgoingLoggers)
@@ -105,7 +171,7 @@ static void logIncomingResponse(const std::shared_ptr<std::vector<std::unique_pt
 /** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors
     Never throws! 
  */
-int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::set<uint16_t>& exportTypes, LWResult *lwr, bool* chained)
+int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers, const std::set<uint16_t>& exportTypes, LWResult *lwr, bool* chained)
 {
   size_t len;
   size_t bufsize=g_outgoingEDNSBufsize;
@@ -165,11 +231,15 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d
     uuid = getUniqueID();
     logOutgoingQuery(outgoingLoggers, context ? context->d_initialRequestId : boost::none, uuid, ip, domain, type, qid, doTCP, vpacket.size(), srcmask);
   }
-#endif
+#endif /* HAVE_PROTOBUF */
+#ifdef HAVE_FSTRM
+  if (isEnabledForQueries(fstrmLoggers)) {
+    logFstreamQuery(fstrmLoggers, queryTime, ip, doTCP, vpacket);
+  }
+#endif /* HAVE_FSTRM */
 
   srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0
 
-  errno=0;
   if(!doTCP) {
     int queryfd;
     if(ip.sin4.sin_family==AF_INET6)
@@ -239,6 +309,13 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d
     return ret;
 
   buf.resize(len);
+
+#ifdef HAVE_FSTRM
+  if (isEnabledForResponses(fstrmLoggers)) {
+    logFstreamResponse(fstrmLoggers, ip, doTCP, buf, queryTime, *now);
+  }
+#endif /* HAVE_FSTRM */
+
   lwr->d_records.clear();
   try {
     lwr->d_tcbit=0;
index 5ddc3af0dab4a2a4b84271e9bb4f1857369ac357..510ae9e23def60897d413a776d77ecc38a54d1e4 100644 (file)
 #include "dns.hh"
 #include "namespaces.hh"
 #include "remote_logger.hh"
+#include "fstrm_logger.hh"
 #include "resolve-context.hh"
 
+
 int asendto(const char *data, size_t len, int flags, const ComboAddress& ip, uint16_t id,
             const DNSName& domain, uint16_t qtype,  int* fd);
 int arecvfrom(std::string& packet, int flags, const ComboAddress& ip, size_t *d_len, uint16_t id,
@@ -68,5 +70,5 @@ public:
   bool d_haveEDNS{false};
 };
 
-int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::set<uint16_t>& exportTypes, LWResult* res, bool* chained);
+int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers, const std::set<uint16_t>& exportTypes, LWResult* res, bool* chained);
 #endif // PDNS_LWRES_HH
index 915eeecb133d7d58553c142cdd82897ee0b01129..5c846d09bd67fb666d975e3de048e3615cf808aa 100644 (file)
 #include <curl/curl.h>
 #include <stdexcept>
 
+void MiniCurl::init()
+{
+  static std::atomic_flag s_init = ATOMIC_FLAG_INIT;
+
+  if (s_init.test_and_set())
+    return;
+
+  CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
+  if (code != 0) {
+    throw std::runtime_error("Error initializing libcurl");
+  }
+}
+
 MiniCurl::MiniCurl(const string& useragent)
 {
   d_curl = curl_easy_init();
-  if (d_curl != nullptr) {
-    curl_easy_setopt(d_curl, CURLOPT_USERAGENT, useragent.c_str());
+  if (d_curl == nullptr) {
+    throw std::runtime_error("Error creating a MiniCurl session");
   }
+  curl_easy_setopt(d_curl, CURLOPT_USERAGENT, useragent.c_str());
 }
 
 MiniCurl::~MiniCurl()
@@ -89,7 +103,7 @@ void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const C
     curl_easy_setopt(d_curl, CURLOPT_INTERFACE, src->toString().c_str());
   }
   curl_easy_setopt(d_curl, CURLOPT_FOLLOWLOCATION, true);
-  /* only allow HTTP, TFTP and SFTP */
+  /* only allow HTTP and HTTPS */
   curl_easy_setopt(d_curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
   curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYPEER, false);
   curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYHOST, false);
@@ -99,8 +113,10 @@ void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const C
   curl_easy_setopt(d_curl, CURLOPT_WRITEDATA, this);
   curl_easy_setopt(d_curl, CURLOPT_TIMEOUT, 2L);
 
+  clearHeaders();
   d_data.clear();
 }
+
 std::string MiniCurl::getURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src)
 {
   setupURL(str, rem, src);
@@ -116,17 +132,46 @@ std::string MiniCurl::getURL(const std::string& str, const ComboAddress* rem, co
   return ret;
 }
 
-std::string MiniCurl::postURL(const std::string& str, const std::string& postdata)
+std::string MiniCurl::postURL(const std::string& str, const std::string& postdata, MiniCurlHeaders& headers)
 {
   setupURL(str);
+  setHeaders(headers);
+  curl_easy_setopt(d_curl, CURLOPT_POSTFIELDSIZE, postdata.size());
   curl_easy_setopt(d_curl, CURLOPT_POSTFIELDS, postdata.c_str());
 
   auto res = curl_easy_perform(d_curl);
+
+  long http_code = 0;
+  curl_easy_getinfo(d_curl, CURLINFO_RESPONSE_CODE, &http_code);
+
   if(res != CURLE_OK)
-    throw std::runtime_error("Unable to post URL");
+    throw std::runtime_error("Unable to post URL ("+std::to_string(http_code)+"): "+string(curl_easy_strerror(res)));
 
   std::string ret=d_data;
 
   d_data.clear();
   return ret;
 }
+
+void MiniCurl::clearHeaders()
+{
+  if (d_curl) {
+    curl_easy_setopt(d_curl, CURLOPT_HTTPHEADER, NULL);
+    if (d_header_list != nullptr) {
+      curl_slist_free_all(d_header_list);
+      d_header_list = nullptr;
+    }
+  }
+}
+
+void MiniCurl::setHeaders(const MiniCurlHeaders& headers)
+{
+  if (d_curl) {
+    for (auto& header : headers) {
+      std::stringstream header_ss;
+      header_ss << header.first << ": " << header.second;
+      d_header_list = curl_slist_append(d_header_list, header_ss.str().c_str());
+    }
+    curl_easy_setopt(d_curl, CURLOPT_HTTPHEADER, d_header_list);
+  }
+}
index 7e913a897de2b16f00e5e109b6b17d0756b3bb7d..a4a366398103536e4efdb4d038c69da1d8f94e05 100644 (file)
 class MiniCurl
 {
 public:
+  using MiniCurlHeaders = std::map<std::string, std::string>;
+
+  static void init();
+
   MiniCurl(const string& useragent="MiniCurl/0.0");
   ~MiniCurl();
   MiniCurl& operator=(const MiniCurl&) = delete;
   std::string getURL(const std::string& str, const ComboAddress* rem=0, const ComboAddress* src=0);
-  std::string postURL(const std::string& str, const std::string& postdata);
+  std::string postURL(const std::string& str, const std::string& postdata, MiniCurlHeaders& headers);
 private:
   CURL *d_curl;
   static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
   std::string d_data;
+  struct curl_slist* d_header_list = nullptr;
   void setupURL(const std::string& str, const ComboAddress* rem=0, const ComboAddress* src=0);
+  void setHeaders(const MiniCurlHeaders& headers);
+  void clearHeaders();
 };
index ee7430471f9aa0b5f3c14e5b25e4b81d083278d8..eb6430491f3bc7738800f1b468b3b1258425b52a 100644 (file)
@@ -136,7 +136,7 @@ try
   if(sock.write((char *) &len, 2) != 2)
     throw PDNSException("tcp write failed");
 
-  sock.writen(string((char*)&*packet.begin(), (char*)&*packet.end()));
+  sock.writen(string(packet.begin(), packet.end()));
   
   if(sock.read((char *) &len, 2) != 2)
     throw PDNSException("tcp read failed");
index 25132682e33b5eb02b1487f48bdfd12549ed1656..21e6d13da1ce9eacf83f80b6515a895229e39072 100644 (file)
@@ -29,7 +29,9 @@ class NSECBitmapGenerator
 public:
   NSECBitmapGenerator(DNSPacketWriter& pw_): pw(pw_)
   {
+    memset(res, 0, sizeof(res));
   }
+
   void set(uint16_t type)
   {
     uint16_t bit = type % 256;
@@ -42,7 +44,7 @@ public:
         tmp.assign(res, res+len+2);
         pw.xfrBlob(tmp);
       }
-      memset(res, 0, 34);
+      memset(res, 0, sizeof(res));
       oldWindow = window;
     }
     res[2+bit/8] |= 1 << (7-(bit%8));
index 267b1344fdcbfe2c9b76cd02b5c177da5c20b3fc..9a5ff229715b95aa5eb7ce4c7d00f5c82ba4e203 100644 (file)
@@ -963,17 +963,11 @@ DNSPacket *PacketHandler::question(DNSPacket *p)
 }
 
 
-void PacketHandler::makeNXDomain(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, SOAData& sd)
+void PacketHandler::makeNXDomain(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, const SOAData& sd)
 {
   DNSZoneRecord rr;
-  rr.dr.d_name=sd.qname;
-  rr.dr.d_type=QType::SOA;
-  rr.dr.d_content=makeSOAContent(sd);
+  rr=makeEditedDNSZRFromSOAData(d_dk, sd, DNSResourceRecord::AUTHORITY);
   rr.dr.d_ttl=min(sd.ttl, sd.default_ttl);
-  rr.signttl=sd.ttl;
-  rr.domain_id=sd.domain_id;
-  rr.dr.d_place=DNSResourceRecord::AUTHORITY;
-  rr.auth = 1;
   r->addRecord(rr);
 
   if(d_dnssec) {
@@ -983,17 +977,11 @@ void PacketHandler::makeNXDomain(DNSPacket* p, DNSPacket* r, const DNSName& targ
   r->setRcode(RCode::NXDomain);
 }
 
-void PacketHandler::makeNOError(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, SOAData& sd, int mode)
+void PacketHandler::makeNOError(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, const SOAData& sd, int mode)
 {
   DNSZoneRecord rr;
-  rr.dr.d_name=sd.qname;
-  rr.dr.d_type=QType::SOA;
-  rr.dr.d_content=makeSOAContent(sd);
+  rr=makeEditedDNSZRFromSOAData(d_dk, sd, DNSResourceRecord::AUTHORITY);
   rr.dr.d_ttl=min(sd.ttl, sd.default_ttl);
-  rr.signttl=sd.ttl;
-  rr.domain_id=sd.domain_id;
-  rr.dr.d_place=DNSResourceRecord::AUTHORITY;
-  rr.auth = 1;
   r->addRecord(rr);
 
   if(d_dnssec) {
@@ -1304,14 +1292,7 @@ DNSPacket *PacketHandler::doQuestion(DNSPacket *p)
     }
 
     if(p->qtype.getCode() == QType::SOA && sd.qname==p->qdomain) {
-      rr.dr.d_name=sd.qname;
-      rr.dr.d_type=QType::SOA;
-      sd.serial = calculateEditSOA(sd.serial, d_dk, sd.qname);
-      rr.dr.d_content=makeSOAContent(sd);
-      rr.dr.d_ttl=sd.ttl;
-      rr.domain_id=sd.domain_id;
-      rr.dr.d_place=DNSResourceRecord::ANSWER;
-      rr.auth = true;
+      rr=makeEditedDNSZRFromSOAData(d_dk, sd);
       r->addRecord(rr);
       goto sendit;
     }
@@ -1420,13 +1401,7 @@ DNSPacket *PacketHandler::doQuestion(DNSPacket *p)
 
     /* Add in SOA if required */
     if(target==sd.qname) {
-        rr.dr.d_name = sd.qname;
-        rr.dr.d_type = QType::SOA;
-        sd.serial = calculateEditSOA(sd.serial, d_dk, sd.qname);
-        rr.dr.d_content = makeSOAContent(sd);
-        rr.dr.d_ttl = sd.ttl;
-        rr.domain_id = sd.domain_id;
-        rr.auth = true;
+        rr=makeEditedDNSZRFromSOAData(d_dk, sd);
         rrset.push_back(rr);
     }
 
index fba95ff50cade3398557d85ae1558dbd4e2bf223..b5bdfc762ccbf154f4a1996024b5378dfccd5f14 100644 (file)
@@ -89,8 +89,8 @@ private:
   int checkUpdatePrerequisites(const DNSRecord *rr, DomainInfo *di);
   void increaseSerial(const string &msgPrefix, const DomainInfo *di, bool haveNSEC3, bool narrow, const NSEC3PARAMRecordContent *ns3pr);
 
-  void makeNXDomain(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, SOAData& sd);
-  void makeNOError(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, SOAData& sd, int mode);
+  void makeNXDomain(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, const SOAData& sd);
+  void makeNOError(DNSPacket* p, DNSPacket* r, const DNSName& target, const DNSName& wildcard, const SOAData& sd, int mode);
   vector<DNSZoneRecord> getBestReferralNS(DNSPacket *p, SOAData& sd, const DNSName &target);
   vector<DNSZoneRecord> getBestDNAMESynth(DNSPacket *p, SOAData& sd, DNSName &target);
   bool tryDNAME(DNSPacket *p, DNSPacket*r, SOAData& sd, DNSName &target);
index 1e171080c00231e789afb398fb6302df39b12c0c..491ae26bd95d34a0c4e3a5c62b2130bd90c0e4cc 100644 (file)
 
 #ifdef HAVE_PROTOBUF
 #include "uuid-utils.hh"
-#endif
+#endif /* HAVE_PROTOBUF */
 
 #include "xpf.hh"
 
@@ -121,6 +121,11 @@ static thread_local std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>
 static thread_local uint64_t t_outgoingProtobufServersGeneration;
 #endif /* HAVE_PROTOBUF */
 
+#ifdef HAVE_FSTRM
+static thread_local std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>> t_frameStreamServers{nullptr};
+static thread_local uint64_t t_frameStreamServersGeneration;
+#endif /* HAVE_FSTRM */
+
 thread_local std::unique_ptr<MT_t> MT; // the big MTasker
 thread_local std::unique_ptr<MemRecursorCache> t_RC;
 thread_local std::unique_ptr<RecursorPacketCache> t_packetCache;
@@ -535,9 +540,6 @@ public:
   {
   }
 
-  typedef set<int> socks_t;
-  socks_t d_socks;
-
   // returning -2 means: temporary OS error (ie, out of files), -1 means error related to remote
   int getSocket(const ComboAddress& toaddr, int* fd)
   {
@@ -547,7 +549,6 @@ public:
 
     if(connect(*fd, (struct sockaddr*)(&toaddr), toaddr.getSocklen()) < 0) {
       int err = errno;
-      //      returnSocket(*fd);
       try {
         closesocket(*fd);
       }
@@ -560,43 +561,32 @@ public:
       return -1;
     }
 
-    d_socks.insert(*fd);
     d_numsocks++;
     return 0;
   }
 
-  void returnSocket(int fd)
-  {
-    socks_t::iterator i=d_socks.find(fd);
-    if(i==d_socks.end()) {
-      throw PDNSException("Trying to return a socket (fd="+std::to_string(fd)+") not in the pool");
-    }
-    returnSocketLocked(i);
-  }
-
   // return a socket to the pool, or simply erase it
-  void returnSocketLocked(socks_t::iterator& i)
+  void returnSocket(int fd)
   {
-    if(i==d_socks.end()) {
-      throw PDNSException("Trying to return a socket not in the pool");
-    }
     try {
-      t_fdm->removeReadFD(*i);
+      t_fdm->removeReadFD(fd);
     }
-    catch(FDMultiplexerException& e) {
+    catch(const FDMultiplexerException& e) {
       // we sometimes return a socket that has not yet been assigned to t_fdm
     }
+
     try {
-      closesocket(*i);
+      closesocket(fd);
     }
     catch(const PDNSException& e) {
       g_log<<Logger::Error<<"Error closing returned UDP socket: "<<e.reason<<endl;
     }
 
-    d_socks.erase(i++);
     --d_numsocks;
   }
 
+private:
+
   // returns -1 for errors which might go away, throws for ones that won't
   static int makeClientSocket(int family)
   {
@@ -629,11 +619,21 @@ public:
       if (::bind(ret, (struct sockaddr *)&sin, sin.getSocklen()) >= 0)
         break;
     }
-    if(!tries)
+
+    if(!tries) {
+      closesocket(ret);
       throw PDNSException("Resolver binding to local query client socket on "+sin.toString()+": "+stringerror());
+    }
+
+    try {
+      setReceiveSocketErrors(ret, family);
+      setNonBlocking(ret);
+    }
+    catch(...) {
+      closesocket(ret);
+      throw;
+    }
 
-    setReceiveSocketErrors(ret, family);
-    setNonBlocking(ret);
     return ret;
   }
 };
@@ -703,7 +703,9 @@ int arecvfrom(std::string& packet, int flags, const ComboAddress& fromaddr, size
 
   int ret=MT->waitEvent(pident, &packet, g_networkTimeoutMsec, now);
 
+  /* -1 means error, 0 means timeout, 1 means a result from handleUDPServerResponse() which might still be an error */
   if(ret > 0) {
+    /* handleUDPServerResponse() will close the socket for us no matter what */
     if(packet.empty()) // means "error"
       return -1;
 
@@ -716,6 +718,7 @@ int arecvfrom(std::string& packet, int flags, const ComboAddress& fromaddr, size
     }
   }
   else {
+    /* getting there means error or timeout, it's up to us to close the socket */
     if(fd >= 0)
       t_udpclientsocks->returnSocket(fd);
   }
@@ -964,6 +967,73 @@ static bool checkOutgoingProtobufExport(LocalStateHolder<LuaConfigItems>& luacon
 
   return true;
 }
+
+#ifdef HAVE_FSTRM
+
+static std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>> startFrameStreamServers(const FrameStreamExportConfig& config)
+{
+  auto result = std::make_shared<std::vector<std::unique_ptr<FrameStreamLogger>>>();
+
+  for (const auto& server : config.servers) {
+    try {
+      std::unordered_map<string,unsigned> options;
+      options["bufferHint"] = config.bufferHint;
+      options["flushTimeout"] = config.flushTimeout;
+      options["inputQueueSize"] = config.inputQueueSize;
+      options["outputQueueSize"] = config.outputQueueSize;
+      options["queueNotifyThreshold"] = config.queueNotifyThreshold;
+      options["reopenInterval"] = config.reopenInterval;
+      FrameStreamLogger *fsl = nullptr;
+      try {
+        ComboAddress address(server);
+        fsl = new FrameStreamLogger(address.sin4.sin_family, address.toStringWithPort(), true, options);
+      }
+      catch (const PDNSException& e) {
+        fsl = new FrameStreamLogger(AF_UNIX, server, true, options);
+      }
+      fsl->setLogQueries(config.logQueries);
+      fsl->setLogResponses(config.logResponses);
+      result->emplace_back(fsl);
+    }
+    catch(const std::exception& e) {
+      g_log<<Logger::Error<<"Error while starting dnstap framestream logger to '"<<server<<": "<<e.what()<<endl;
+    }
+    catch(const PDNSException& e) {
+      g_log<<Logger::Error<<"Error while starting dnstap framestream logger to '"<<server<<": "<<e.reason<<endl;
+    }
+  }
+
+  return result;
+}
+
+static bool checkFrameStreamExport(LocalStateHolder<LuaConfigItems>& luaconfsLocal)
+{
+  if (!luaconfsLocal->frameStreamExportConfig.enabled) {
+    if (t_frameStreamServers) {
+      // dt's take care of cleanup
+      t_frameStreamServers.reset();
+    }
+
+    return false;
+  }
+
+  /* if the server was not running, or if it was running according to a
+     previous configuration */
+  if (!t_frameStreamServers ||
+      t_frameStreamServersGeneration < luaconfsLocal->generation) {
+
+    if (t_frameStreamServers) {
+      // dt's take care of cleanup
+      t_frameStreamServers.reset();
+    }
+
+    t_frameStreamServers = startFrameStreamServers(luaconfsLocal->frameStreamExportConfig);
+    t_frameStreamServersGeneration = luaconfsLocal->generation;
+  }
+
+  return true;
+}
+#endif /* HAVE_FSTRM */
 #endif /* HAVE_PROTOBUF */
 
 #ifdef NOD_ENABLED
@@ -1092,6 +1162,10 @@ static void startDoResolve(void *p)
     }
 #endif /* HAVE_PROTOBUF */
 
+#ifdef HAVE_FSTRM
+    checkFrameStreamExport(luaconfsLocal);
+#endif
+
     DNSPacketWriter pw(packet, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_mdp.d_qclass);
 
     pw.getHeader()->aa=0;
@@ -1147,7 +1221,9 @@ static void startDoResolve(void *p)
     sr.setInitialRequestId(dc->d_uuid);
     sr.setOutgoingProtobufServers(t_outgoingProtobufServers);
 #endif
-
+#ifdef HAVE_FSTRM
+    sr.setFrameStreamServers(t_frameStreamServers);
+#endif
     sr.setQuerySource(dc->d_remote, g_useIncomingECS && !dc->d_ednssubnet.source.empty() ? boost::optional<const EDNSSubnetOpts&>(dc->d_ednssubnet) : boost::none);
 
     bool tracedQuery=false; // we could consider letting Lua know about this too
@@ -1918,6 +1994,10 @@ static void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
         needECS = true;
       }
       logQuery = t_protobufServers && luaconfsLocal->protobufExportConfig.logQueries;
+#endif /* HAVE_PROTOBUF */
+
+#ifdef HAVE_FSTRM
+      checkFrameStreamExport(luaconfsLocal);
 #endif
 
       if(needECS || needXPF || (t_pdl && (t_pdl->d_gettag_ffi || t_pdl->d_gettag))) {
@@ -2112,6 +2192,9 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr
   }
   logQuery = t_protobufServers && luaconfsLocal->protobufExportConfig.logQueries;
   bool logResponse = t_protobufServers && luaconfsLocal->protobufExportConfig.logResponses;
+#endif
+#ifdef HAVE_FSTRM
+  checkFrameStreamExport(luaconfsLocal);
 #endif
   EDNSSubnetOpts ednssubnet;
   bool ecsFound = false;
@@ -3242,6 +3325,8 @@ static void handleUDPServerResponse(int fd, FDMultiplexer::funcparam_t& var)
 retryWithName:
 
   if(!MT->sendEvent(pident, &packet)) {
+    /* we did not find a match for this response, something is wrong */
+
     // we do a full scan for outstanding queries on unexpected answers. not too bad since we only accept them on the right port number, which is hard enough to guess
     for(MT_t::waiters_t::iterator mthread=MT->d_waiters.begin(); mthread!=MT->d_waiters.end(); ++mthread) {
       if(pident.fd==mthread->key.fd && mthread->key.remote==pident.remote &&  mthread->key.type == pident.type &&
@@ -3264,6 +3349,7 @@ retryWithName:
     }
   }
   else if(fd >= 0) {
+    /* we either found a waiter (1) or encountered an issue (-1), it's up to us to clean the socket anyway */
     t_udpclientsocks->returnSocket(fd);
   }
 }
@@ -4154,6 +4240,9 @@ try
   checkProtobufExport(luaconfsLocal);
   checkOutgoingProtobufExport(luaconfsLocal);
 #endif /* HAVE_PROTOBUF */
+#ifdef HAVE_FSTRM
+  checkFrameStreamExport(luaconfsLocal);
+#endif
 
   PacketID pident;
 
index 30af9d11b2bf6e21dec96045623d8c15b4fd7e3e..a1b0995a50796f8cf299e2313dd3b7389caa6e7c 100644 (file)
@@ -244,7 +244,10 @@ int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone, const vect
 
   DomainInfo di;
   try {
-    B.getDomainInfo(zone, di);
+    if (!B.getDomainInfo(zone, di)) {
+      cout<<"[Error] Unable to get domain information for zone '"<<zone<<"'"<<endl;
+      return 1;
+    }
   } catch(const PDNSException &e) {
     if (di.kind == DomainInfo::Slave) {
       cout<<"[Error] non-IP address for masters: "<<e.reason<<endl;
@@ -1585,7 +1588,6 @@ bool showZone(DNSSECKeeper& dk, const DNSName& zone, bool exportDS = false)
 
     sort(keys.begin(),keys.end());
     reverse(keys.begin(),keys.end());
-    bool shown=false;
     for(const auto& key : keys) {
       string algname = DNSSECKeeper::algorithm2name(key.d_algorithm);
 
@@ -1602,9 +1604,6 @@ bool showZone(DNSSECKeeper& dk, const DNSName& zone, bool exportDS = false)
         cout << "DNSKEY = " <<zone.toString()<<" IN DNSKEY "<< key.getZoneRepresentation() << "; ( " + algname + " ) " <<endl;
       }
 
-      if (shown) continue;
-      shown=true;
-
       const std::string prefix(exportDS ? "" : "DS = ");
       cout<<prefix<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, DNSSECKeeper::SHA1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
       cout<<prefix<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, DNSSECKeeper::SHA256).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
@@ -2085,7 +2084,7 @@ try
       return 0;
     }
     try {
-      SSQLite3 db(cmds[1], true); // create=ok
+      SSQLite3 db(cmds[1], "", true); // create=ok
       vector<string> statements;
       stringtok(statements, sqlCreate, ";");
       for(const string& statement :  statements) {
index e8147def7977006bc25a8b5c632e31ce5d69d1fa..8d7eab185f15a4342a1cca8cb1050bc1b9eb824b 100644 (file)
@@ -131,6 +131,10 @@ public:
     LUA=65402
   };
 
+  QType(typeenum orig) : code(orig)
+  {
+  }
+
   typedef pair<string,uint16_t> namenum;
   static vector<namenum> names;
 
diff --git a/pdns/rec-dnstap.hh b/pdns/rec-dnstap.hh
new file mode 100644 (file)
index 0000000..217bcc9
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#ifdef HAVE_FSTRM
+#include "dnstap.hh"
+#endif /* HAVE_FSTRM */
+
+class RecDnstapMessage : public DnstapMessage
+{
+public:
+  RecDnstapMessage(const std::string& identity, const ComboAddress* requestor, const ComboAddress* responder, bool isTCP, const char* packet, const size_t len, const struct timespec* queryTime, const struct timespec* responseTime)
+      : DnstapMessage(identity, requestor, responder, isTCP, packet, len, queryTime, responseTime) {
+    const struct dnsheader* dh = reinterpret_cast<const struct dnsheader*>(packet);
+    dnstap::Message* message = proto_message.mutable_message();
+    message->set_type(!dh->qr ? dnstap::Message_Type_RESOLVER_QUERY : dnstap::Message_Type_RESOLVER_RESPONSE);
+  }
+};
index ea1d56ed6c9d42934aa71b5df2d93fe682c1580e..1b7dfe6fdbb749aa00fb660eef041d658b8b3737 100644 (file)
@@ -145,6 +145,43 @@ static void parseProtobufOptions(boost::optional<protobufOptions_t> vars, Protob
 }
 #endif /* HAVE_PROTOBUF */
 
+#ifdef HAVE_FSTRM
+typedef std::unordered_map<std::string, boost::variant<bool, uint64_t, std::string, std::vector<std::pair<int,std::string> > > > frameStreamOptions_t;
+
+static void parseFrameStreamOptions(boost::optional<frameStreamOptions_t> vars, FrameStreamExportConfig& config)
+{
+  if (!vars) {
+    return;
+  }
+
+  if (vars->count("logQueries")) {
+    config.logQueries = boost::get<bool>((*vars)["logQueries"]);
+  }
+  if (vars->count("logResponses")) {
+    config.logResponses = boost::get<bool>((*vars)["logResponses"]);
+  }
+
+  if (vars->count("bufferHint")) {
+    config.bufferHint = boost::get<uint64_t>((*vars)["bufferHint"]);
+  }
+  if (vars->count("flushTimeout")) {
+    config.flushTimeout = boost::get<uint64_t>((*vars)["flushTimeout"]);
+  }
+  if (vars->count("inputQueueSize")) {
+    config.inputQueueSize = boost::get<uint64_t>((*vars)["inputQueueSize"]);
+  }
+  if (vars->count("outputQueueSize")) {
+    config.outputQueueSize = boost::get<uint64_t>((*vars)["outputQueueSize"]);
+  }
+  if (vars->count("queueNotifyThreshold")) {
+    config.queueNotifyThreshold = boost::get<uint64_t>((*vars)["queueNotifyThreshold"]);
+  }
+  if (vars->count("reopenInterval")) {
+    config.reopenInterval = boost::get<uint64_t>((*vars)["reopenInterval"]);
+  }
+}
+#endif /* HAVE_FSTRM */
+
 void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& delayedThreads)
 {
   LuaConfigItems lci;
@@ -495,6 +532,42 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de
     });
 #endif
 
+#ifdef HAVE_FSTRM
+  Lua.writeFunction("dnstapFrameStreamServer", [&lci](boost::variant<const std::string, const std::unordered_map<int, std::string>> servers, boost::optional<frameStreamOptions_t> vars) {
+      if (!lci.frameStreamExportConfig.enabled) {
+
+        lci.frameStreamExportConfig.enabled = true;
+
+          try {
+            if (servers.type() == typeid(std::string)) {
+              auto server = boost::get<const std::string>(servers);
+              if (!boost::starts_with(server, "/")) {
+                ComboAddress parsecheck(server);
+              }
+              lci.frameStreamExportConfig.servers.emplace_back(server);
+            }
+            else {
+              auto serversMap = boost::get<const std::unordered_map<int,std::string>>(servers);
+              for (const auto& serverPair : serversMap) {
+                lci.frameStreamExportConfig.servers.emplace_back(serverPair.second);
+              }
+            }
+
+            parseFrameStreamOptions(vars, lci.frameStreamExportConfig);
+          }
+          catch(std::exception& e) {
+            g_log<<Logger::Error<<"Error reading config for dnstap framestream logger: "<<e.what()<<endl;
+          }
+          catch(PDNSException& e) {
+            g_log<<Logger::Error<<"Error reading config for dnstap framestream logger: "<<e.reason<<endl;
+          }
+      }
+      else {
+        g_log<<Logger::Error<<"Only one dnstapFrameStreamServer() directive can be configured, we already have "<<lci.frameStreamExportConfig.servers.at(0)<<endl;
+      }
+    });
+#endif /* HAVE_FSTRM */
+
   try {
     Lua.executeCode(ifs);
     g_luaconfs.setState(lci);
index 4323bd0c6cc9b5bbde4750bba9a2bb54b3856ff2..1cd2e62046cd9a9ee7d9c27c39df4eb38719b7dd 100644 (file)
@@ -41,6 +41,20 @@ struct ProtobufExportConfig
   bool taggedOnly{false};
 };
 
+struct FrameStreamExportConfig
+{
+  std::vector<string> servers;
+  bool enabled{false};
+  bool logQueries{true};
+  bool logResponses{true};
+  unsigned bufferHint{0};
+  unsigned flushTimeout{0};
+  unsigned inputQueueSize{0};
+  unsigned outputQueueSize{0};
+  unsigned queueNotifyThreshold{0};
+  unsigned reopenInterval{0};
+};
+
 struct TrustAnchorFileInfo {
   uint32_t interval{24};
   std::string fname;
@@ -57,6 +71,8 @@ public:
   map<DNSName,std::string> negAnchors;
   ProtobufExportConfig protobufExportConfig;
   ProtobufExportConfig outgoingProtobufExportConfig;
+  FrameStreamExportConfig frameStreamExportConfig;
+
   /* we need to increment this every time the configuration
      is reloaded, so we know if we need to reload the protobuf
      remote loggers */
index 0c86d515be4f71bf91fbc585f516894e73071e85..bd0f7cf9b11d58a6511eac5a6b7a4d866312f3c8 100644 (file)
 #include "dnsrecords.hh"
 #include "version.hh"
 
+#ifdef HAVE_LUA_RECORDS
+#include "minicurl.hh"
+#endif /* HAVE_LUA_RECORDS */
+
 time_t s_starttime;
 
 string s_programname="pdns"; // used in packethandler.cc
@@ -468,6 +472,10 @@ int main(int argc, char **argv)
     /* setup rng */
     dns_random_init();
 
+#ifdef HAVE_LUA_RECORDS
+    MiniCurl::init();
+#endif /* HAVE_LUA_RECORDS */
+
     if(!::arg()["load-modules"].empty()) {
       vector<string> modules;
 
@@ -590,8 +598,7 @@ int main(int argc, char **argv)
       }
     }
 
-    if(!::arg().mustDo("disable-tcp"))
-      TN=new TCPNameserver; 
+    TN = make_unique<TCPNameserver>();
   }
   catch(const ArgException &A) {
     g_log<<Logger::Error<<"Fatal error: "<<A.reason<<endl;
index b7636f11b6c8379277ff20e97b344a8a97004713..4f3f8a2199f2db6ec07d1071eab4b489fcfc6512 100644 (file)
@@ -52,3 +52,5 @@ html-docs
 doctrees
 latex
 PowerDNS-Recursor.pdf
+/*.pb.cc
+/*.pb.h
index 9d1faba044ac987482530ecab1f219a206a5bf3d..12d93f680fd8040b3b8f54757be5ebd3a7d334ca 100644 (file)
@@ -18,6 +18,11 @@ AM_CXXFLAGS += \
        -DNODCACHEDIR=\"$(nodcachedir)\"
 endif
 
+if FSTRM
+AM_CPPFLAGS += \
+       $(FSTRM_CFLAGS)
+endif
+
 AM_LDFLAGS = \
        $(PROGRAM_LDFLAGS) \
        $(THREADFLAGS)
@@ -59,6 +64,7 @@ EXTRA_DIST = \
        NOTICE \
        opensslsigners.hh opensslsigners.cc \
        portsmplexer.cc \
+       dnstap.proto dnstap.cc dnstap.hh fstrm_logger.cc fstrm_logger.hh rec-dnstap.hh \
        rrd/* \
        html incfiles \
        test_libcrypto \
@@ -109,6 +115,7 @@ pdns_recursor_SOURCES = \
        ednsoptions.cc ednsoptions.hh \
        ednssubnet.cc ednssubnet.hh \
        filterpo.cc filterpo.hh \
+       fstrm_logger.cc fstrm_logger.hh \
        gettime.cc gettime.hh \
        gss_context.cc gss_context.hh \
        iputils.hh iputils.cc \
@@ -370,18 +377,41 @@ if HAVE_PROTOC
 dnsmessage.pb.cc: dnsmessage.proto
        $(AM_V_GEN)$(PROTOC) --cpp_out=./ $<
 
+if FSTRM
+dnstap.pb.cc: dnstap.proto
+       $(AM_V_GEN)$(PROTOC) -I$(srcdir) --cpp_out=./ $<
+endif
+
+
 BUILT_SOURCES += dnsmessage.pb.cc
 pdns_recursor_LDADD += $(PROTOBUF_LIBS)
 nodist_pdns_recursor_SOURCES = dnsmessage.pb.cc dnsmessage.pb.h
+nodist_testrunner_SOURCES = dnsmessage.pb.cc dnsmessage.pb.h
+
+if FSTRM
+BUILT_SOURCES += dnstap.pb.cc
+pdns_recursor.$(OBJEXT): dnstap.pb.cc dnsmessage.pb.cc
+testrunner$(OBJEXT): dnstap.pb.cc dnsmessage.pb.cc
+nodist_pdns_recursor_SOURCES += dnstap.pb.cc dnstap.pb.h
+nodist_testrunner_SOURCES += dnstap.pb.cc dnstap.pb.h
+else
 pdns_recursor.$(OBJEXT): dnsmessage.pb.cc
+testrunner$(OBJEXT): dnsmessage.pb.cc
+endif
 
-nodist_testrunner_SOURCES = dnsmessage.pb.cc dnsmessage.pb.h
 testrunner_LDADD += $(PROTOBUF_LIBS)
-testrunner$(OBJEXT): dnsmessage.pb.cc
 
 endif
 endif
 
+if FSTRM
+pdns_recursor_SOURCES += \
+       dnstap.cc dnstap.hh rec-dnstap.hh
+
+pdns_recursor_LDADD += \
+       $(FSTRM_LIBS)
+endif
+
 rec_control_SOURCES = \
        arguments.cc arguments.hh \
        dnsname.hh dnsname.cc \
index fbad126428bc35290896463ca950f6146df323de..3d2b20f555f5b1e27ded837b07a238f158f4391a 100644 (file)
@@ -149,6 +149,8 @@ AC_ARG_WITH([nod-cache-dir],
   [nodcachedir="$withval"]
 )
 
+PDNS_CHECK_DNSTAP
+
 AC_MSG_CHECKING([whether we will enable compiler security checks])
 AC_ARG_ENABLE([hardening],
   [AS_HELP_STRING([--disable-hardening], [disable compiler security checks @<:@default=no@:>@])],
@@ -245,5 +247,9 @@ AM_COND_IF([NOD_ENABLED],
   [AC_MSG_NOTICE([nod: yes])],
   [AC_MSG_NOTICE([nod: no])]
 )
+AM_COND_IF([FSTRM],
+  [AC_MSG_NOTICE([dnstap: yes])],
+  [AC_MSG_NOTICE([dnstap: no])]
+)
 AC_MSG_NOTICE([Context library: $pdns_context_library])
 AC_MSG_NOTICE([])
diff --git a/pdns/recursordist/dnstap.cc b/pdns/recursordist/dnstap.cc
new file mode 120000 (symlink)
index 0000000..06c8e37
--- /dev/null
@@ -0,0 +1 @@
+../dnstap.cc
\ No newline at end of file
diff --git a/pdns/recursordist/dnstap.hh b/pdns/recursordist/dnstap.hh
new file mode 120000 (symlink)
index 0000000..9fd70f0
--- /dev/null
@@ -0,0 +1 @@
+../dnstap.hh
\ No newline at end of file
diff --git a/pdns/recursordist/dnstap.proto b/pdns/recursordist/dnstap.proto
new file mode 120000 (symlink)
index 0000000..6b6dfbd
--- /dev/null
@@ -0,0 +1 @@
+../dnstap.proto
\ No newline at end of file
index 6a13b76b50abff67f11e851b5625086ce38cb320..87fd7aea2438a1f732261799e7a0a4627b420701 100644 (file)
@@ -1,6 +1,25 @@
 Changelogs for 4.1.x
 ====================
 
+.. changelog::
+  :version: 4.1.13
+  :released: 21st of May 2019
+
+  .. change::
+    :tags: Improvements, Performance
+    :pullreq: 7673
+    :tickets: 7661
+
+    Add the ``disable-real-memory-usage`` setting to skip expensive
+    collection of detailed memory usage info.
+
+  .. change::
+    :tags: Bug Fixes, DNSSEC
+    :pullreq: 7816
+    :tickets: 7714
+
+    Fix DNSSEC validation of wildcards expanded onto themselves.
+
 .. changelog::
   :version: 4.1.12
   :released: 2nd of April 2019
index fb85f956a27fe4b274d80937aeb84fdeabbd2175..9fbeb8bdb068dda88f9d645e527300f60e1cf0d7 100644 (file)
@@ -1,6 +1,29 @@
 Changelogs for 4.2.x
 ====================
 
+.. changelog::
+  :version: 4.2.0-rc1
+  :released: 23th of May 2019
+
+  .. change::
+    :tags: Bug Fixes, SNMP
+    :pullreq: 7826
+
+    Fix the detection of ``snmp_select_info2()``.
+
+  .. change::
+    :tags: Bug Fixes, Internals
+    :pullreq: 7813
+    :tickets: 7272
+
+    Ensure a valid range to ``string()`` in ``PacketReader::getUnquotedText()``
+
+  .. change::
+    :tags: Improvements, SNMP
+    :pullreq: 7818
+
+    Use ``net-snmp-config --netsnmp-agent-libs`` instead of ``--agent-libs``.
+
 .. changelog::
   :version: 4.2.0-beta1
   :released: 7th of May 2019
index 629f63047e69cef42890c66c577e00581f5317b2..bb35e44b24789cb53b6a637061d3577303573939 100644 (file)
@@ -95,3 +95,35 @@ Protobol Buffers Definition
 The protocol buffers message types can be found in the `dnsmessage.proto <https://github.com/PowerDNS/pdns/blob/master/pdns/dnsmessage.proto>`_ file and is included here:
 
 .. literalinclude:: ../../../dnsmessage.proto
+
+Logging in ``dnstap`` format using framestreams
+-----------------------------------------------
+Define the following function to enable logging of outgoing queries and/or responses in ``dnstap`` format.
+The recursor must have been built with configure ``--enable-dnstap`` to make this feature available.
+
+.. function:: dnstapFrameStreamServer(servers, [, options])
+
+  .. versionadded:: 4.3.0
+
+  Send dnstap formatted message to one or more framestream servers for outgoing queries and/or incoming responses.
+
+  :param servers: Either a pathname of a unix domain socket starting with a slash or the IP:port to connect to, or a list of those. If more than one server is configured, all messages are sent to every server.
+  :type servers: string or list of strings
+  :param table options: A table with ``key=value`` pairs with options.
+
+  Options:
+
+  * ``logQueries=true``: bool - log oputgoing queries
+  * ``logResponses=true``: bool - log incoming responses
+  The follwing options apply to the settings of the framestream library. Refer to the documentation of that
+  library for the default values, exact description and allowable values for these options.
+  For all these options, absence or a zero value has the effect of using the library-provided default value.
+
+  * ``bufferHint=0``: unsigned
+  * ``flushTimeout=0``: unsigned
+  * ``inputQueueSize=0``: unsigned
+  * ``outputQueueSize=0``: unsigned
+  * ``queueNotifyThreshold=0``: unsigned
+  * ``reopenInterval=0``: unsigned
+
diff --git a/pdns/recursordist/docs/nod_udr.rst b/pdns/recursordist/docs/nod_udr.rst
new file mode 100644 (file)
index 0000000..28d6f65
--- /dev/null
@@ -0,0 +1,67 @@
+Newly Observed Domain Tracking
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A common security technique for detecting domains that may be suspicious or be associated with bad actors such as hosting malware, phishing or botnet command and control, is to investigate domains that haven't been seen before, i.e. are newly observed.
+
+Deciding whether a domain is truly a new domain would involve deterministic methods, such as maintaining a database of all domains ever seen, and comparing all domain lookups against that database. Such a mechanism would not be scalable in a recursor, and so is best suited to offline analysis. However, determining candidate domains for such an offline service is a problem that can be solved in the recursor, given that sending all domain lookups to such an offline service would still be prohibitely costly, and given that the true number of newly observed domains is likely to be relatively small in a given time period.
+
+A simple method to determine a candidate domain would simply be to check if the domain was not in the recursor cache; indeed this is a method used by many security researchers. However, while that does produce a smaller list of candidate domains, cache misses are still relatively common, particularly in deployments where techniques such as EDNS client-subnet are used.
+
+Therefore, a feature has been developed for the recursor which uses probablistic data structures (specifically a Stable Bloom Filter (SBF): [http://webdocs.cs.ualberta.ca/~drafiei/papers/DupDet06Sigmod.pdf]). This recursor feature is named "Newly Observed Domain" or "NOD" for short.
+
+The use of a probablistic data structure means that the memory and CPU usage for the NOD feature is minimal, however it does mean that there can be false positives (a domain flagged as new when it is not), and false negatives (a domain that is new is not detected). The size of the SBF data structure can be tuned to reduce the FP/FN rate, although it is created with a default size (67108864 cells) that should provide a reasonably low FP/FN rate. To configure a different size use the ``new-domain-db-size`` setting to specify a higher or lower cell count. Each cell consumes 1-bit of RAM (per recursor thread) and 1-byte of disk space. 
+
+NOD is disabled by default, and must be enabled through the use of the following setting in recursor.conf:
+
+``new-domain-tracking=yes``
+
+Once enabled the recursor will keep track of previously seen domains using the SBF data structure, which is periodically persisted to the directory specified in the ``new-domain-history-dir``, which defaults to /var/lib/pdns-recursor/nod.
+
+Administrators may wish to prevent certain domains or subdomains from ever triggering the NOD algorithm, in which case those domains must be added to the ``new-domain-whitelist`` setting as a comma separated list. No domain (or subdomain of a domain) listed will be considered a newly observed domain.
+
+There are several ways to receive the information about newly observed domains:
+
+Logging
++++++++
+
+The setting ``new-domain-log`` is enabled by default once the NOD feature is enabled, and will log the newly observed domain to the recursor logfile.
+
+DNS Lookup
+++++++++++
+
+The setting ``new-domain-lookup=<base domain>`` will cause the recursor to isse a DNS A record lookup to ``<newly observed domain>.<base domain>``. This can be a suitable method to send NOD data to an offsite or remote partner, however care should be taken to ensure that data is not leaked inadvertently.
+
+Protobuf Logging
+++++++++++++++++
+
+If both NOD and protobuf logging are enabled, then the ``newlyObservedDomain`` field of the protobuf message emitted by the recursor will be set to true. Additionally newly observed domains will be tagged in the protobuf stream using the tag ``pdns-nod`` by default. The setting ``new-domain-pb-tag=<tag>`` can be used to alter the tag.
+
+Unique Domain Response
+~~~~~~~~~~~~~~~~~~~~~~
+
+A similar feature to NOD is Unique Domain Response (UDR). This feature uses the same probablistic data structures as NOD to store information about unique responses for a given lookup domain. Determining if a particular response is unique for a given lookup domain is extremly useful for determining potential security issues such as:
+
+* Fast-Flux Domain Names
+* Cache-Poisoning Attacks
+* Botnet Command and Control Servers
+  etc.
+
+This is because well-behaved domains tend to return fairly stable results to DNS record lookups, and thus domains which don't exhibit this behaviour may be suspsicious or may indicate a domain under attack.
+
+UDR is disabled by default - to enable it, set ``unique-response-tracking=yes`` in recursor.conf.
+
+The data is persisted to /var/log/pdns-recursor/udr by default, which can be changed with the setting ``unique-response-history-dir=<new directory>``.
+
+The SBF (which is maintained separately per recursor thread) cell size defaults to 67108864, which can be changed using the setting ``unique-response-db-size``. The same caveats regarding FPs/FNs apply as for NOD.
+
+Similarly to NOD, unique domain responses can be tracked using several mechanisms:
+
+Logging
++++++++
+
+The setting ``unique-response-log`` is enabled by default once the NOD feature is enabled, and will log the newly observed domain to the recursor logfile.
+
+Protobuf Logging
+++++++++++++++++
+
+If both UDR and protobuf logging are enabled, then unique domain responses will be tagged in the protobuf stream using the tag ``pdns-udr`` by default. The setting ``unique-response-pb-tag=<tag>`` can be used to alter the tag.
index a6c80527c6892651d3311d6215eed1f13fc72691..17667f8ff20eca65618362efc13afce1cc852e4e 100644 (file)
@@ -37,3 +37,7 @@ There are three levels of throttling.
 .. _securitypolling:
 
 .. include:: common/secpoll.rst
+
+.. _nod_udr:
+
+.. include:: nod_udr.rst
diff --git a/pdns/recursordist/fstrm_logger.cc b/pdns/recursordist/fstrm_logger.cc
new file mode 120000 (symlink)
index 0000000..e66c9cc
--- /dev/null
@@ -0,0 +1 @@
+../fstrm_logger.cc
\ No newline at end of file
diff --git a/pdns/recursordist/fstrm_logger.hh b/pdns/recursordist/fstrm_logger.hh
new file mode 120000 (symlink)
index 0000000..b4898e9
--- /dev/null
@@ -0,0 +1 @@
+../fstrm_logger.hh
\ No newline at end of file
index c913094e46729ce129a3f189aff6c694bef06437..2624b90fa5fb77b59e8a9d76ca0a8b520f0da983 100644 (file)
@@ -22,9 +22,7 @@
 <!-- ========================= CONTENT ========================= -->
 
 <div class="topbar">
-    <label>API Key: <input type="password" id="password" value=""></label>
-    <button>Submit</button><!-- Just a trick to make the user click outside of the input field -->
-    <span id="connection-status" style="display:none">No server connection or incorrect key</span>
+    <span id="connection-status" style="display:none">No server connection</span>
     <span id="connection-error"></span>
 </div>
 
index 9e6fc03190c29b6e6cc8712f30e59f174a34cc0d..549102c0e2c139c61315e6de0b8072df5ab59dd1 100644 (file)
@@ -21,12 +21,6 @@ $(document).ready(function () {
         $('#' + name).html(h);
     };
 
-    var password = $("#password").val();
-    $("#password").change(function (e) {
-        password = $("#password").val();
-        update();
-    });
-
     var qpsgraph = new Rickshaw.Graph({
         element: document.getElementById("qpschart"),
         width: 400,
@@ -70,7 +64,6 @@ $(document).ready(function () {
 
     var jsonstatParams = function (command, name, filtered) {
         var d = {
-            'api-key': password,
             'command': command,
             'name': name
         };
@@ -159,7 +152,7 @@ $(document).ready(function () {
 
     function update() {
         $.ajax({
-            url: 'api/v1/servers/localhost/statistics?api-key=' + password,
+            url: 'api/v1/servers/localhost/statistics',
             type: 'GET',
             dataType: 'json',
             success: function (adata, x, y) {
@@ -216,7 +209,7 @@ $(document).ready(function () {
 
         if (!version) {
             $.ajax({
-                url: 'api/v1/servers/localhost?api-key=' + password, type: 'GET', dataType: 'json',
+                url: 'api/v1/servers/localhost', type: 'GET', dataType: 'json',
                 success: function (data) {
                     version = "PowerDNS " + data["daemon_type"] + " " + data["version"];
                 }
diff --git a/pdns/recursordist/m4/pdns_check_dnstap.m4 b/pdns/recursordist/m4/pdns_check_dnstap.m4
new file mode 120000 (symlink)
index 0000000..ed71845
--- /dev/null
@@ -0,0 +1 @@
+../../../m4/pdns_check_dnstap.m4
\ No newline at end of file
diff --git a/pdns/recursordist/rec-dnstap.hh b/pdns/recursordist/rec-dnstap.hh
new file mode 120000 (symlink)
index 0000000..4011f47
--- /dev/null
@@ -0,0 +1 @@
+../rec-dnstap.hh
\ No newline at end of file
index ffaa2ce20035573623741f4eb1165776efdab342..aabfe33fe0c53e24a579c7998d937a3ce154103e 100644 (file)
@@ -32,7 +32,7 @@ bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& reque
   return false;
 }
 
-int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::set<uint16_t>& exportTypes, LWResult* res, bool* chained)
+int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers,const std::set<uint16_t>& exportTypes, LWResult* res, bool* chained)
 {
   return 0;
 }
index f060a009f077c00431cf24eca82498d526c169f6..1797d3080899509a8fd49ab59a6e858454c526c1 100644 (file)
@@ -961,7 +961,7 @@ int PacketHandler::processUpdate(DNSPacket *p) {
       DNSResourceRecord rec;
       di.backend->lookup(QType(QType::ANY), rr->d_name, nullptr, di.id);
       while (di.backend->get(rec)) {
-        if (rec.qtype != QType::CNAME && rec.qtype != QType::RRSIG) {
+        if (rec.qtype != QType::CNAME && rec.qtype != QType::ENT && rec.qtype != QType::RRSIG) {
           // leave database handle in a consistent state
           while (di.backend->get(rec))
             ;
index a6d4ec84d34e92dfb156b2096866b09c98a4a877..641008582ef363bfb33d43daba9d06ca79e52592 100644 (file)
@@ -123,7 +123,7 @@ try
       len = htons(packet.size());
       if(sock.write((char *) &len, 2) != 2)
         throw PDNSException("tcp write failed");
-      sock.writen(string((char*)&*packet.begin(), (char*)&*packet.end()));
+      sock.writen(string((char*)&packet[0], packet.size()));
       if(sock.read((char *) &len, 2) != 2)
         throw PDNSException("tcp read failed");
 
@@ -177,7 +177,7 @@ try
   if(sock.write((char *) &len, 2) != 2)
     throw PDNSException("tcp write failed");
 
-  sock.writen(string((char*)&*packet.begin(), (char*)&*packet.end()));
+  sock.writen(string(packet.begin(), packet.end()));
 
   bool isNSEC3 = false;
   int soacount=0;
index 9d0b8d5c83b9ff818f153dbfae8087575a1c6ed9..28a24f678ae17e963a356ed7449d9d0264e3684c 100644 (file)
 #include "statbag.hh"
 #include <boost/array.hpp>
 #include "ednssubnet.hh"
+
+#ifdef HAVE_LIBCURL
+#include "minicurl.hh"
+#endif
+
 StatBag S;
 
 bool hidettl=false;
@@ -24,7 +29,7 @@ string ttl(uint32_t ttl)
 
 void usage() {
   cerr<<"sdig"<<endl;
-  cerr<<"Syntax: sdig IP-ADDRESS PORT QUESTION QUESTION-TYPE [dnssec] [recurse] [showflags] [hidesoadetails] [hidettl] [tcp] [ednssubnet SUBNET/MASK] [xpf XPFDATA]"<<endl;
+  cerr<<"Syntax: sdig IP-ADDRESS-OR-DOH-URL PORT QUESTION QUESTION-TYPE [dnssec] [ednssubnet SUBNET/MASK] [hidesoadetails] [hidettl] [recurse] [showflags] [tcp] [xpf XPFDATA]"<<endl;
 }
 
 const string nameForClass(uint16_t qclass, uint16_t qtype)
@@ -48,6 +53,7 @@ try
   bool tcp=false;
   bool showflags=false;
   bool hidesoadetails=false;
+  bool doh=false;
   boost::optional<Netmask> ednsnm;
   uint16_t xpfcode = 0, xpfversion = 0, xpfproto = 0;
   char *xpfsrc = NULL, *xpfdst = NULL;
@@ -149,9 +155,27 @@ try
   }
 
   string reply;
-  ComboAddress dest(argv[1] + (*argv[1]=='@'), atoi(argv[2]));
+  string question(packet.begin(), packet.end());
+  ComboAddress dest;
+  if(*argv[1]=='h') {
+    doh = true;
+  }
+  else {
+    dest = ComboAddress(argv[1] + (*argv[1]=='@'), atoi(argv[2]));
+  }
 
-  if(tcp) {
+  if(doh) {
+#ifdef HAVE_LIBCURL
+    MiniCurl mc;
+    MiniCurl::MiniCurlHeaders mch;
+    mch.insert(std::make_pair("Content-Type", "application/dns-message"));
+    mch.insert(std::make_pair("Accept", "application/dns-message"));
+    reply = mc.postURL(argv[1], question, mch);
+#else
+    throw PDNSException("please link sdig against libcurl for DoH support");
+#endif
+  }
+  else if(tcp) {
     Socket sock(dest.sin4.sin_family, SOCK_STREAM);
     sock.connect(dest);
     uint16_t len;
@@ -159,7 +183,7 @@ try
     if(sock.write((char *) &len, 2) != 2)
       throw PDNSException("tcp write failed");
 
-    sock.writen(string((char*)&*packet.begin(), (char*)&*packet.end()));
+    sock.writen(question);
     
     if(sock.read((char *) &len, 2) != 2)
       throw PDNSException("tcp read failed");
@@ -181,7 +205,7 @@ try
   else //udp
   {
     Socket sock(dest.sin4.sin_family, SOCK_DGRAM);
-    sock.sendTo(string((char*)&*packet.begin(), (char*)&*packet.end()), dest);
+    sock.sendTo(question, dest);
     int result=waitForData(sock.getHandle(), 10);
     if(result < 0) 
       throw std::runtime_error("Error waiting for data: "+string(strerror(errno)));
@@ -249,7 +273,9 @@ try
           cerr<<"EDNS Subnet response: "<<reso.source.toString()<<", scope: "<<reso.scope.toString()<<", family = "<<reso.scope.getNetwork().sin4.sin_family<<endl;
        }
       }
-
+      else if(iter->first == EDNSOptionCode::PADDING) {
+        cerr<<"EDNS Padding size: "<<(iter->second.size())<<endl;
+      }
       else {
         cerr<<"Have unknown option "<<(int)iter->first<<endl;
       }
index 094e8a8a31c8197603fb0297bd0a73f96393f00e..05300ee5346c204568a45eb87652a686e15c74c4 100644 (file)
@@ -156,3 +156,23 @@ bool makeIncreasedSOARecord(SOAData& sd, const string& increaseKind, const strin
 
   return true;
 }
+
+DNSZoneRecord makeEditedDNSZRFromSOAData(DNSSECKeeper& dk, const SOAData& sd, DNSResourceRecord::Place place) {
+  SOAData edited = sd;
+  edited.serial = calculateEditSOA(sd.serial, dk, sd.qname);
+
+  DNSRecord soa;
+  soa.d_name = sd.qname;
+  soa.d_type = QType::SOA;
+  soa.d_ttl = sd.ttl;
+  soa.d_place = place;
+  soa.d_content = makeSOAContent(edited);
+
+  DNSZoneRecord dzr;
+  dzr.domain_id = sd.domain_id;
+  dzr.signttl = sd.ttl;
+  dzr.auth = true;
+  dzr.dr = soa;
+
+  return dzr;
+}
index 8adeb28d24350cee7c0d20c622af31575d41ec40..eb7075388d1af4f7a925f61d8460b6bdc731455b 100644 (file)
@@ -180,7 +180,7 @@ private:
 };
 
 // Constructor.
-SSQLite3::SSQLite3( const std::string & database, bool creat )
+SSQLite3::SSQLite3( const std::string & database, const std::string & journalmode, bool creat )
 {
   if (access( database.c_str(), F_OK ) == -1){
     if (!creat)
@@ -195,6 +195,9 @@ SSQLite3::SSQLite3( const std::string & database, bool creat )
   m_dolog = 0;
   m_in_transaction = false;
   sqlite3_busy_handler(m_pDB, busyHandler, 0);
+
+  if(journalmode.length())
+    execute("PRAGMA journal_mode="+journalmode);
 }
 
 void SSQLite3::setLog(bool state)
index 9b02caba5431ac91b37577c85601f115bbebd820..5768337b20396bb3224d34ab75c7d52b5de5871c 100644 (file)
@@ -37,7 +37,7 @@ private:
 protected:
 public:
   //! Constructor.
-  SSQLite3( const std::string & database, bool creat=false );
+  SSQLite3( const std::string & database, const std::string & journalmode, bool creat=false);
 
   //! Destructor.
   ~SSQLite3();
index afffa27b432e8bcc75ec3029cef86aba12cd66cf..1da9644a2d9b7bebac56d2eedecba2d558ad2099 100644 (file)
@@ -38,11 +38,13 @@ try
     }
   }
 
-  if(argc < 2) {
+  if(argc <= 1) {
     usage();
     exit(EXIT_FAILURE);
   }
 
+  string type(argc == 2 ? "A" : argv[2]);
+
   ::arg().set("resolver","Use this resolver for ALIAS and the internal stub resolver")="no"; 
 
   reportAllTypes();
@@ -50,7 +52,7 @@ try
 
   vector<DNSZoneRecord> ret;
 
-  int res=stubDoResolve(DNSName(argv[1]), DNSRecordContent::TypeToNumber(argv[2]), ret);
+  int res=stubDoResolve(DNSName(argv[1]), DNSRecordContent::TypeToNumber(type), ret);
 
   cout<<"res: "<<res<<endl;
   for(const auto& r : ret) {
index 5dd3c98a39fefc6d2d0baecf51328714b50a1d25..dcdcc1ce0ba9ac811186099f3d2ddbff4c7080fc 100644 (file)
@@ -502,7 +502,7 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
       ret = d_asyncResolve(ip, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, res, chained);
     }
     else {
-      ret=asyncresolve(ip, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, d_outgoingProtobufServers, luaconfsLocal->outgoingProtobufExportConfig.exportTypes, res, chained);
+      ret=asyncresolve(ip, sendQname, type, doTCP, sendRDQuery, EDNSLevel, now, srcmask, ctx, d_outgoingProtobufServers, d_frameStreamServers, luaconfsLocal->outgoingProtobufExportConfig.exportTypes, res, chained);
     }
     if(ret < 0) {
       return ret; // transport error, nothing to learn here
@@ -689,12 +689,9 @@ struct speedOrderCA
 vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly)
 {
   typedef vector<DNSRecord> res_t;
-  res_t res;
-
   typedef vector<ComboAddress> ret_t;
   ret_t ret;
 
-  QType type;
   bool oldCacheOnly = d_cacheonly;
   bool oldRequireAuthData = d_requireAuthData;
   bool oldValidationRequested = d_DNSSECValidationRequested;
@@ -702,48 +699,44 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
   d_DNSSECValidationRequested = false;
   d_cacheonly = cacheOnly;
 
-  for(int j=1; j<2+s_doIPv6; j++)
-  {
-    bool done=false;
-    switch(j) {
-      case 0:
-        type = QType::ANY;
-        break;
-      case 1:
-        type = QType::A;
-        break;
-      case 2:
-        type = QType::AAAA;
-        break;
-    }
-
-    vState newState = Indeterminate;
-    if(!doResolve(qname, type, res,depth+1, beenthere, newState) && !res.empty()) {  // this consults cache, OR goes out
-      for(res_t::const_iterator i=res.begin(); i!= res.end(); ++i) {
-        if(i->d_type == QType::A || i->d_type == QType::AAAA) {
-         if(auto rec = getRR<ARecordContent>(*i))
-           ret.push_back(rec->getCA(53));
-         else if(auto aaaarec = getRR<AAAARecordContent>(*i))
-           ret.push_back(aaaarec->getCA(53));
-          done=true;
+  vState newState = Indeterminate;
+  res_t resv4;
+  // If IPv4 ever becomes second class, we should revisit this
+  if (doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
+    for (auto const &i : resv4) {
+      if (i.d_type == QType::A) {
+        if (auto rec = getRR<ARecordContent>(i)) {
+          ret.push_back(rec->getCA(53));
         }
       }
     }
-    if(done) {
-      if(j==1 && s_doIPv6) { // we got an A record, see if we have some AAAA lying around
-       vector<DNSRecord> cset;
-       if(t_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote) > 0) {
-         for(auto k=cset.cbegin();k!=cset.cend();++k) {
-           if(k->d_ttl > (unsigned int)d_now.tv_sec ) {
-             if (auto drc = getRR<AAAARecordContent>(*k)) {
-               ComboAddress ca=drc->getCA(53);
-               ret.push_back(ca);
-             }
-           }
-         }
-       }
+  }
+  if (s_doIPv6) {
+    if (ret.empty()) {
+      // We did not find IPv4 addresses, try to get IPv6 ones
+      newState = Indeterminate;
+      res_t resv6;
+      if (doResolve(qname, QType::AAAA, resv6, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
+        for (const auto &i : resv6) {
+          if (i.d_type == QType::AAAA) {
+            if (auto rec = getRR<AAAARecordContent>(i))
+              ret.push_back(rec->getCA(53));
+          }
+        }
+      }
+    } else {
+      // We have some IPv4 records, don't bother with going out to get IPv6, but do consult the cache
+      // Once IPv6 adoption matters, this needs to be revisited
+      res_t cset;
+      if (t_RC->get(d_now.tv_sec, qname, QType(QType::AAAA), false, &cset, d_cacheRemote) > 0) {
+        for (const auto &i : cset) {
+          if (i.d_ttl > (unsigned int)d_now.tv_sec ) {
+            if (auto rec = getRR<AAAARecordContent>(i)) {
+              ret.push_back(rec->getCA(53));
+            }
+          }
+        }
       }
-      break;
     }
   }
 
@@ -764,7 +757,7 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
   t_sstorage.nsSpeeds[qname].purge(speeds);
 
   if(ret.size() > 1) {
-    random_shuffle(ret.begin(), ret.end(), dns_random);
+    random_shuffle(ret.begin(), ret.end());
     speedOrderCA so(speeds);
     stable_sort(ret.begin(), ret.end(), so);
 
@@ -806,6 +799,8 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto
     *flawedNSSet = false;
 
     if(t_RC->get(d_now.tv_sec, subdomain, QType(QType::NS), false, &ns, d_cacheRemote) > 0) {
+      bestns.reserve(ns.size());
+
       for(auto k=ns.cbegin();k!=ns.cend(); ++k) {
         if(k->d_ttl > (unsigned int)d_now.tv_sec ) {
           vector<DNSRecord> aset;
@@ -1381,44 +1376,37 @@ bool SyncRes::moreSpecificThan(const DNSName& a, const DNSName &b) const
 
 struct speedOrder
 {
-  speedOrder(map<DNSName,double> &speeds) : d_speeds(speeds) {}
-  bool operator()(const DNSName &a, const DNSName &b) const
+  bool operator()(const std::pair<DNSName, double> &a, const std::pair<DNSName, double> &b) const
   {
-    return d_speeds[a] < d_speeds[b];
+    return a.second < b.second;
   }
-  map<DNSName, double>& d_speeds;
 };
 
-inline vector<DNSName> SyncRes::shuffleInSpeedOrder(NsSet &tnameservers, const string &prefix)
+inline std::vector<std::pair<DNSName, double>> SyncRes::shuffleInSpeedOrder(NsSet &tnameservers, const string &prefix)
 {
-  vector<DNSName> rnameservers;
+  std::vector<std::pair<DNSName, double>> rnameservers;
   rnameservers.reserve(tnameservers.size());
   for(const auto& tns: tnameservers) {
-    rnameservers.push_back(tns.first);
+    double speed = t_sstorage.nsSpeeds[tns.first].get(&d_now);
+    rnameservers.push_back({tns.first, speed});
     if(tns.first.empty()) // this was an authoritative OOB zone, don't pollute the nsSpeeds with that
       return rnameservers;
   }
-  map<DNSName, double> speeds;
 
-  for(const auto& val: rnameservers) {
-    double speed;
-    speed=t_sstorage.nsSpeeds[val].get(&d_now);
-    speeds[val]=speed;
-  }
-  random_shuffle(rnameservers.begin(),rnameservers.end(), dns_random);
-  speedOrder so(speeds);
+  random_shuffle(rnameservers.begin(),rnameservers.end());
+  speedOrder so;
   stable_sort(rnameservers.begin(),rnameservers.end(), so);
 
   if(doLog()) {
     LOG(prefix<<"Nameservers: ");
-    for(vector<DNSName>::const_iterator i=rnameservers.begin();i!=rnameservers.end();++i) {
-                       if(i!=rnameservers.begin()) {
+    for(auto i=rnameservers.begin();i!=rnameservers.end();++i) {
+      if(i!=rnameservers.begin()) {
         LOG(", ");
         if(!((i-rnameservers.begin())%3)) {
           LOG(endl<<prefix<<"             ");
         }
       }
-      LOG(i->toLogString()<<"(" << (boost::format("%0.2f") % (speeds[*i]/1000.0)).str() <<"ms)");
+      LOG(i->first.toLogString()<<"(" << (boost::format("%0.2f") % (i->second/1000.0)).str() <<"ms)");
     }
     LOG(endl);
   }
@@ -1436,7 +1424,7 @@ inline vector<ComboAddress> SyncRes::shuffleForwardSpeed(const vector<ComboAddre
     speed=t_sstorage.nsSpeeds[nsName].get(&d_now);
     speeds[val]=speed;
   }
-  random_shuffle(nameservers.begin(),nameservers.end(), dns_random);
+  random_shuffle(nameservers.begin(),nameservers.end());
   speedOrderCA so(speeds);
   stable_sort(nameservers.begin(),nameservers.end(), so);
 
@@ -1581,25 +1569,25 @@ bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAd
   return false;
 }
 
-vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<DNSName >::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<DNSName >& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly)
+vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, std::vector<std::pair<DNSName, double>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, double>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly)
 {
   vector<ComboAddress> result;
 
-  if(!tns->empty()) {
-    LOG(prefix<<qname<<": Trying to resolve NS '"<<*tns<< "' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl);
-    result = getAddrs(*tns, depth+2, beenthere, cacheOnly);
+  if(!tns->first.empty()) {
+    LOG(prefix<<qname<<": Trying to resolve NS '"<<tns->first<< "' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl);
+    result = getAddrs(tns->first, depth+2, beenthere, cacheOnly);
     pierceDontQuery=false;
   }
   else {
     LOG(prefix<<qname<<": Domain has hardcoded nameserver");
 
-    if(nameservers[*tns].first.size() > 1) {
+    if(nameservers[tns->first].first.size() > 1) {
       LOG("s");
     }
     LOG(endl);
 
-    sendRDQuery = nameservers[*tns].second;
-    result = shuffleForwardSpeed(nameservers[*tns].first, doLog() ? (prefix+qname.toString()+": ") : string(), sendRDQuery);
+    sendRDQuery = nameservers[tns->first].second;
+    result = shuffleForwardSpeed(nameservers[tns->first].first, doLog() ? (prefix+qname.toString()+": ") : string(), sendRDQuery);
     pierceDontQuery=true;
   }
   return result;
@@ -2977,12 +2965,15 @@ bool SyncRes::doResolveAtThisIP(const std::string& prefix, const DNSName& qname,
   if(lwr.d_tcbit) {
     *truncated = true;
 
-    if (doTCP && !dontThrottle) {
+    if (doTCP) {
       LOG(prefix<<qname<<": truncated bit set, over TCP?"<<endl);
-      /* let's treat that as a ServFail answer from this server */
-      t_sstorage.throttle.throttle(d_now.tv_sec, boost::make_tuple(remoteIP, qname, qtype.getCode()), 60, 3);
+      if (!dontThrottle) {
+        /* let's treat that as a ServFail answer from this server */
+        t_sstorage.throttle.throttle(d_now.tv_sec, boost::make_tuple(remoteIP, qname, qtype.getCode()), 60, 3);
+      }
       return false;
     }
+    LOG(prefix<<qname<<": truncated bit set, over UDP"<<endl);
 
     return true;
   }
@@ -3146,7 +3137,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
   LOG(endl);
 
   for(;;) { // we may get more specific nameservers
-    vector<DNSName > rnameservers = shuffleInSpeedOrder(nameservers, doLog() ? (prefix+qname.toString()+": ") : string() );
+    auto rnameservers = shuffleInSpeedOrder(nameservers, doLog() ? (prefix+qname.toString()+": ") : string() );
 
     for(auto tns=rnameservers.cbegin();;++tns) {
       if(tns==rnameservers.cend()) {
@@ -3162,7 +3153,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
 
       bool cacheOnly = false;
       // this line needs to identify the 'self-resolving' behaviour
-      if(qname == *tns && (qtype.getCode() == QType::A || qtype.getCode() == QType::AAAA)) {
+      if(qname == tns->first && (qtype.getCode() == QType::A || qtype.getCode() == QType::AAAA)) {
         /* we might have a glue entry in cache so let's try this NS
            but only if we have enough in the cache to know how to reach it */
         LOG(prefix<<qname<<": Using NS to resolve itself, but only using what we have in cache ("<<(1+tns-rnameservers.cbegin())<<"/"<<rnameservers.size()<<")"<<endl);
@@ -3176,11 +3167,11 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
       bool sendRDQuery=false;
       boost::optional<Netmask> ednsmask;
       LWResult lwr;
-      const bool wasForwarded = tns->empty() && (!nameservers[*tns].first.empty());
+      const bool wasForwarded = tns->first.empty() && (!nameservers[tns->first].first.empty());
       int rcode = RCode::NoError;
       bool gotNewServers = false;
 
-      if(tns->empty() && !wasForwarded) {
+      if(tns->first.empty() && !wasForwarded) {
         LOG(prefix<<qname<<": Domain is out-of-band"<<endl);
         /* setting state to indeterminate since validation is disabled for local auth zone,
            and Insecure would be misleading. */
@@ -3203,13 +3194,13 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
         remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly);
 
         if(remoteIPs.empty()) {
-          LOG(prefix<<qname<<": Failed to get IP for NS "<<*tns<<", trying next if available"<<endl);
+          LOG(prefix<<qname<<": Failed to get IP for NS "<<tns->first<<", trying next if available"<<endl);
           flawedNSSet=true;
           continue;
         }
         else {
           bool hitPolicy{false};
-          LOG(prefix<<qname<<": Resolved '"<<auth<<"' NS "<<*tns<<" to: ");
+          LOG(prefix<<qname<<": Resolved '"<<auth<<"' NS "<<tns->first<<" to: ");
           for(remoteIP = remoteIPs.cbegin(); remoteIP != remoteIPs.cend(); ++remoteIP) {
             if(remoteIP != remoteIPs.cbegin()) {
               LOG(", ");
@@ -3233,18 +3224,18 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
 
           bool truncated = false;
           bool gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery,
-                                             *tns, *remoteIP, false, &truncated);
+                                             tns->first, *remoteIP, false, &truncated);
           if (gotAnswer && truncated ) {
             /* retry, over TCP this time */
             gotAnswer = doResolveAtThisIP(prefix, qname, qtype, lwr, ednsmask, auth, sendRDQuery,
-                                          *tns, *remoteIP, true, &truncated);
+                                          tns->first, *remoteIP, true, &truncated);
           }
 
           if (!gotAnswer) {
             continue;
           }
 
-          LOG(prefix<<qname<<": Got "<<(unsigned int)lwr.d_records.size()<<" answers from "<<*tns<<" ("<< remoteIP->toString() <<"), rcode="<<lwr.d_rcode<<" ("<<RCode::to_s(lwr.d_rcode)<<"), aa="<<lwr.d_aabit<<", in "<<lwr.d_usec/1000<<"ms"<<endl);
+          LOG(prefix<<qname<<": Got "<<(unsigned int)lwr.d_records.size()<<" answers from "<<tns->first<<" ("<< remoteIP->toString() <<"), rcode="<<lwr.d_rcode<<" ("<<RCode::to_s(lwr.d_rcode)<<"), aa="<<lwr.d_aabit<<", in "<<lwr.d_usec/1000<<"ms"<<endl);
 
           /*  // for you IPv6 fanatics :-)
               if(remoteIP->sin4.sin_family==AF_INET6)
@@ -3252,7 +3243,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
           */
           //        cout<<"msec: "<<lwr.d_usec/1000.0<<", "<<g_avgLatency/1000.0<<'\n';
 
-          t_sstorage.nsSpeeds[tns->empty()? DNSName(remoteIP->toStringWithPort()) : *tns].submit(*remoteIP, lwr.d_usec, &d_now);
+          t_sstorage.nsSpeeds[tns->first.empty()? DNSName(remoteIP->toStringWithPort()) : tns->first].submit(*remoteIP, lwr.d_usec, &d_now);
 
           /* we have received an answer, are we done ? */
           bool done = processAnswer(depth, lwr, qname, qtype, auth, wasForwarded, ednsmask, sendRDQuery, nameservers, ret, luaconfsLocal->dfe, &gotNewServers, &rcode, state);
index b316c92d4bf4de8473cdfd87a4a40d7c1ddbb484..58390eca6b8061a86f454bc45d2623ef9bb809e0 100644 (file)
@@ -57,6 +57,9 @@
 
 #ifdef HAVE_PROTOBUF
 #include <boost/uuid/uuid.hpp>
+#ifdef HAVE_FSTRM
+#include "fstrm_logger.hh"
+#endif /* HAVE_FSTRM */
 #endif
 
 extern GlobalStateHolder<SuffixMatchNode> g_dontThrottleNames;
@@ -679,6 +682,13 @@ public:
   }
 #endif
 
+#ifdef HAVE_FSTRM
+  void setFrameStreamServers(std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& servers)
+  {
+    d_frameStreamServers = servers;
+  }
+#endif /* HAVE_FSTRM */
+
   void setAsyncCallback(asyncresolve_t func)
   {
     d_asyncResolve = func;
@@ -782,7 +792,7 @@ private:
   void getBestNSFromCache(const DNSName &qname, const QType &qtype, vector<DNSRecord>&bestns, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>& beenthere);
   DNSName getBestNSNamesFromCache(const DNSName &qname, const QType &qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>&beenthere);
 
-  inline vector<DNSName> shuffleInSpeedOrder(NsSet &nameservers, const string &prefix);
+  inline vector<std::pair<DNSName, double>> shuffleInSpeedOrder(NsSet &nameservers, const string &prefix);
   inline vector<ComboAddress> shuffleForwardSpeed(const vector<ComboAddress> &rnameservers, const string &prefix, const bool wasRd);
   bool moreSpecificThan(const DNSName& a, const DNSName &b) const;
   vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly);
@@ -791,7 +801,7 @@ private:
   bool nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress&);
   bool throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType& qtype, bool pierceDontQuery);
 
-  vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<DNSName >::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<DNSName >& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly);
+  vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<std::pair<DNSName, double>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, double>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly);
 
   void sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, bool rdQuery);
   RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask>, vState& state, bool& needWildcardProof, bool& gatherWildcardProof, unsigned int& wildcardLabelsCount, bool sendRDQuery);
@@ -830,6 +840,7 @@ private:
   shared_ptr<RecursorLua4> d_pdl;
   boost::optional<Netmask> d_outgoingECSNetwork;
   std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>> d_outgoingProtobufServers{nullptr};
+  std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>> d_frameStreamServers{nullptr};
 #ifdef HAVE_PROTOBUF
   boost::optional<const boost::uuids::uuid&> d_initialRequestId;
 #endif
index ec31ecff8da54512b85050d3ffa4ad12bd4239f7..dd82281a7a81331f63b3abe98ab7eda7d6d49e62 100644 (file)
@@ -16,6 +16,7 @@ public:
   virtual size_t write(const void* buffer, size_t bufferSize, unsigned int writeTimeout) = 0;
   virtual IOState tryWrite(std::vector<uint8_t>& buffer, size_t& pos, size_t toWrite) = 0;
   virtual IOState tryRead(std::vector<uint8_t>& buffer, size_t& pos, size_t toRead) = 0;
+  virtual std::string getServerNameIndication() = 0;
   virtual void close() = 0;
 
 protected:
@@ -275,18 +276,12 @@ public:
     }
   }
 
-  bool writeSizeAndMsg(const void* buffer, size_t bufferSize, unsigned int writeTimeout)
+  std::string getServerNameIndication()
   {
     if (d_conn) {
-      uint16_t size = htons(bufferSize);
-      if (d_conn->write(&size, sizeof(size), writeTimeout) != sizeof(size)) {
-        return false;
-      }
-      return (d_conn->write(buffer, bufferSize, writeTimeout) == bufferSize);
-    }
-    else {
-      return sendSizeAndMsgWithTimeout(d_socket, bufferSize, static_cast<const char*>(buffer), writeTimeout, nullptr, nullptr, 0, 0, 0);
+      return d_conn->getServerNameIndication();
     }
+    return std::string();
   }
 
 private:
index b9160c072d58a5628ec23b3649a14d9d73796970..f78229196519d09cbea18f58d8382bc388283252 100644 (file)
@@ -537,25 +537,6 @@ namespace {
     bool d_auth;
   };
 
-  DNSZoneRecord makeEditedDNSZRFromSOAData(DNSSECKeeper& dk, const SOAData& sd)
-  {
-    SOAData edited = sd;
-    edited.serial = calculateEditSOA(sd.serial, dk, sd.qname);
-
-    DNSRecord soa;
-    soa.d_name = sd.qname;
-    soa.d_type = QType::SOA;
-    soa.d_ttl = sd.ttl;
-    soa.d_place = DNSResourceRecord::ANSWER;
-    soa.d_content = makeSOAContent(edited);
-
-    DNSZoneRecord dzr;
-    dzr.auth = true;
-    dzr.dr = soa;
-
-    return dzr;
-  }
-
   shared_ptr<DNSPacket> getFreshAXFRPacket(shared_ptr<DNSPacket> q)
   {
     shared_ptr<DNSPacket> ret = shared_ptr<DNSPacket>(q->replyPacket());
index f238f4e24d3d490e591ca3b3bbc02b9ca10922c7..82fba8763afbac36fee6541af81da942b299b926 100644 (file)
@@ -47,7 +47,7 @@ public:
     if(d_rsock.write((char *) &len, 2) != 2)
       throw PDNSException("tcp write failed");
 
-    d_rsock.writen(string((char*)&*packet.begin(), (char*)&*packet.end()));
+    d_rsock.writen(string(packet.begin(), packet.end()));
     
     int bread=d_rsock.read((char *) &len, 2);
     if( bread <0)
index 9ecfc34998edc13b8540203b9450ef17157230e1..ce2f3963ce8079367b94c21433cfc275ef48f816 100644 (file)
@@ -117,7 +117,10 @@ void showBuildConfiguration()
     "PKCS#11 " <<
 #endif
 #ifdef HAVE_PROTOBUF
-    "protobuf " <<
+"protobuf " <<
+#endif
+#ifdef HAVE_FSTRM
+"dnstap-framestream " <<
 #endif
 #ifdef REMOTEBACKEND_ZEROMQ
     "remotebackend-zeromq " <<
index a168559e178505720390c4f6c9957c6f8bc8ce17..fa8df4837f9086117613c400aa79e9e911853755 100644 (file)
@@ -125,17 +125,26 @@ static bool optionsHandler(HttpRequest* req, HttpResponse* resp) {
   return false;
 }
 
-static void apiWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp, const string &apikey) {
+void WebServer::apiWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp, bool allowPassword) {
   if (optionsHandler(req, resp)) return;
 
   resp->headers["access-control-allow-origin"] = "*";
 
-  if (apikey.empty()) {
+  if (d_apikey.empty()) {
     g_log<<Logger::Error<<req->logprefix<<"HTTP API Request \"" << req->url.path << "\": Authentication failed, API Key missing in config" << endl;
     throw HttpUnauthorizedException("X-API-Key");
   }
-  bool auth_ok = req->compareHeader("x-api-key", apikey) || req->getvars["api-key"] == apikey;
-  
+
+  bool auth_ok = req->compareHeader("x-api-key", d_apikey) || req->getvars["api-key"] == d_apikey;
+
+  if (!auth_ok && allowPassword) {
+    if (!d_webserverPassword.empty()) {
+      auth_ok = req->compareAuthorization(d_webserverPassword);
+    } else {
+      auth_ok = true;
+    }
+  }
+
   if (!auth_ok) {
     g_log<<Logger::Error<<req->logprefix<<"HTTP Request \"" << req->url.path << "\": Authentication by API Key failed" << endl;
     throw HttpUnauthorizedException("X-API-Key");
@@ -169,15 +178,14 @@ static void apiWrapper(WebServer::HandlerFunction handler, HttpRequest* req, Htt
   }
 }
 
-void WebServer::registerApiHandler(const string& url, HandlerFunction handler) {
-  HandlerFunction f = boost::bind(&apiWrapper, handler, _1, _2, d_apikey);
+void WebServer::registerApiHandler(const string& url, HandlerFunction handler, bool allowPassword) {
+  HandlerFunction f = boost::bind(&WebServer::apiWrapper, this, handler, _1, _2, allowPassword);
   registerBareHandler(url, f);
-  d_registerApiHandlerCalled = true;
 }
 
-static void webWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp, const string &password) {
-  if (!password.empty()) {
-    bool auth_ok = req->compareAuthorization(password);
+void WebServer::webWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp) {
+  if (!d_webserverPassword.empty()) {
+    bool auth_ok = req->compareAuthorization(d_webserverPassword);
     if (!auth_ok) {
       g_log<<Logger::Debug<<req->logprefix<<"HTTP Request \"" << req->url.path << "\": Web Authentication failed" << endl;
       throw HttpUnauthorizedException("Basic");
@@ -188,7 +196,7 @@ static void webWrapper(WebServer::HandlerFunction handler, HttpRequest* req, Htt
 }
 
 void WebServer::registerWebHandler(const string& url, HandlerFunction handler) {
-  HandlerFunction f = boost::bind(&webWrapper, handler, _1, _2, d_webserverPassword);
+  HandlerFunction f = boost::bind(&WebServer::webWrapper, this, handler, _1, _2);
   registerBareHandler(url, f);
 }
 
index 500f157044fb358e0607b5b067ae3c6343a8d214..d7848847f3aab4dc75ab8a4c8f3d2af247b0bd14 100644 (file)
@@ -158,16 +158,10 @@ public:
   virtual ~WebServer() { };
 
   void setApiKey(const string &apikey) {
-    if (d_registerApiHandlerCalled) {
-      throw PDNSException("registerApiHandler has been called, can not change apikey");
-    }
     d_apikey = apikey;
   }
 
   void setPassword(const string &password) {
-    if (d_registerWebHandlerCalled) {
-      throw PDNSException("registerWebHandler has been called, can not change password");
-    }
     d_webserverPassword = password;
   }
 
@@ -182,7 +176,7 @@ public:
   void handleRequest(HttpRequest& request, HttpResponse& resp) const;
 
   typedef boost::function<void(HttpRequest* req, HttpResponse* resp)> HandlerFunction;
-  void registerApiHandler(const string& url, HandlerFunction handler);
+  void registerApiHandler(const string& url, HandlerFunction handler, bool allowPassword=false);
   void registerWebHandler(const string& url, HandlerFunction handler);
 
   enum class LogLevel : uint8_t {
@@ -233,10 +227,9 @@ protected:
   std::shared_ptr<Server> d_server;
 
   std::string d_apikey;
-  bool d_registerApiHandlerCalled{false};
-
+  void apiWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp, bool allowPassword);
   std::string d_webserverPassword;
-  bool d_registerWebHandlerCalled{false};
+  void webWrapper(WebServer::HandlerFunction handler, HttpRequest* req, HttpResponse* resp);
 
   NetmaskGroup d_acl;
 
index e3f32a5099eb93aea126e64406127e5645db37d2..349876b74ea8bef7c752d6d28e0de7ad75096675 100644 (file)
@@ -522,7 +522,7 @@ static void validateGatheredRRType(const DNSResourceRecord& rr) {
   }
 }
 
-static void gatherRecords(const Json container, const DNSName& qname, const QType qtype, const int ttl, vector<DNSResourceRecord>& new_records, vector<DNSResourceRecord>& new_ptrs) {
+static void gatherRecords(const string& logprefix, const Json container, const DNSName& qname, const QType qtype, const int ttl, vector<DNSResourceRecord>& new_records, vector<DNSResourceRecord>& new_ptrs) {
   UeberBackend B;
   DNSResourceRecord rr;
   rr.qname = qname;
@@ -558,6 +558,9 @@ static void gatherRecords(const Json container, const DNSName& qname, const QTyp
 
     if ((rr.qtype.getCode() == QType::A || rr.qtype.getCode() == QType::AAAA) &&
         boolFromJson(record, "set-ptr", false) == true) {
+
+      g_log<<Logger::Warning<<logprefix<<"API call uses deprecated set-ptr feature, please remove it"<<endl;
+
       DNSResourceRecord ptr;
       makePtr(rr, &ptr);
 
@@ -1552,7 +1555,7 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) {
         }
         if (rrset["records"].is_array()) {
           int ttl = intFromJson(rrset, "ttl");
-          gatherRecords(rrset, qname, qtype, ttl, new_records, new_ptrs);
+          gatherRecords(req->logprefix, rrset, qname, qtype, ttl, new_records, new_ptrs);
         }
         if (rrset["comments"].is_array()) {
           gatherComments(rrset, qname, qtype, new_comments);
@@ -1983,7 +1986,7 @@ static void patchZone(HttpRequest* req, HttpResponse* resp) {
           // ttl shouldn't be part of DELETE, and it shouldn't be required if we don't get new records.
           int ttl = intFromJson(rrset, "ttl");
           // new_ptrs is merged.
-          gatherRecords(rrset, qname, qtype, ttl, new_records, new_ptrs);
+          gatherRecords(req->logprefix, rrset, qname, qtype, ttl, new_records, new_ptrs);
 
           for(DNSResourceRecord& rr : new_records) {
             rr.domain_id = di.id;
@@ -2007,8 +2010,10 @@ static void patchZone(HttpRequest* req, HttpResponse* resp) {
           di.backend->lookup(QType(QType::ANY), qname);
           DNSResourceRecord rr;
           while (di.backend->get(rr)) {
-            if (qtype.getCode() == 0) {
+            if (rr.qtype.getCode() == QType::ENT) {
               ent_present = true;
+              /* that's fine, we will override it */
+              continue;
             }
             if (qtype.getCode() != rr.qtype.getCode()
               && (exclusiveEntryTypes.count(qtype.getCode()) != 0
index bf4038a2a51a92fbd10aa9489134dd855368e34e..fefb03bb06bd4ba2ae2e0ced85d3484cef59567c 100644 (file)
@@ -467,16 +467,16 @@ RecursorWebServer::RecursorWebServer(FDMultiplexer* fdm)
   d_ws->bind();
 
   // legacy dispatch
-  d_ws->registerApiHandler("/jsonstat", boost::bind(&RecursorWebServer::jsonstat, this, _1, _2));
+  d_ws->registerApiHandler("/jsonstat", boost::bind(&RecursorWebServer::jsonstat, this, _1, _2), true);
   d_ws->registerApiHandler("/api/v1/servers/localhost/cache/flush", &apiServerCacheFlush);
   d_ws->registerApiHandler("/api/v1/servers/localhost/config/allow-from", &apiServerConfigAllowFrom);
   d_ws->registerApiHandler("/api/v1/servers/localhost/config", &apiServerConfig);
   d_ws->registerApiHandler("/api/v1/servers/localhost/rpzstatistics", &apiServerRPZStats);
   d_ws->registerApiHandler("/api/v1/servers/localhost/search-data", &apiServerSearchData);
-  d_ws->registerApiHandler("/api/v1/servers/localhost/statistics", &apiServerStatistics);
+  d_ws->registerApiHandler("/api/v1/servers/localhost/statistics", &apiServerStatistics, true);
   d_ws->registerApiHandler("/api/v1/servers/localhost/zones/<id>", &apiServerZoneDetail);
   d_ws->registerApiHandler("/api/v1/servers/localhost/zones", &apiServerZones);
-  d_ws->registerApiHandler("/api/v1/servers/localhost", &apiServerDetail);
+  d_ws->registerApiHandler("/api/v1/servers/localhost", &apiServerDetail, true);
   d_ws->registerApiHandler("/api/v1/servers", &apiServer);
   d_ws->registerApiHandler("/api", &apiDiscovery);
 
index 4ef9c1ca26ba2115a4ffd926dc072cca9c75d3d5..56d5e3e7b2d2bad68ed062e3e18fce95dc36a5d4 100755 (executable)
@@ -20,6 +20,7 @@ SQLITE_DB = 'pdns.sqlite3'
 WEBPORT = 5556
 DNSPORT = 5300
 APIKEY = '1234567890abcdefghijklmnopq-key'
+WEBPASSWORD = 'something'
 PDNSUTIL_CMD = [os.environ.get("PDNSUTIL", "../pdns/pdnsutil"), "--config-dir=."]
 
 NAMED_CONF_TPL = """
@@ -103,7 +104,8 @@ pdns_recursor = os.environ.get("PDNSRECURSOR", "../pdns/recursordist/pdns_recurs
 common_args = [
     "--daemon=no", "--socket-dir=.", "--config-dir=.",
     "--local-address=127.0.0.1", "--local-port="+str(DNSPORT),
-    "--webserver=yes", "--webserver-port="+str(WEBPORT), "--webserver-address=127.0.0.1", "--webserver-password=something",
+    "--webserver=yes", "--webserver-port="+str(WEBPORT), "--webserver-address=127.0.0.1",
+    "--webserver-password="+WEBPASSWORD,
     "--api-key="+APIKEY
 ]
 
@@ -188,6 +190,7 @@ returncode = 0
 test_env = {}
 test_env.update(os.environ)
 test_env.update({
+    'WEBPASSWORD': WEBPASSWORD,
     'WEBPORT': str(WEBPORT),
     'APIKEY': APIKEY,
     'DAEMON': daemon,
index eca56505de2955124cd218f42d5a026178342022..f180e55e471c0265a56c4694222688f7c51799eb 100644 (file)
@@ -10,6 +10,10 @@ class TestBasics(ApiTestCase):
         r = requests.get(self.url("/api/v1/servers/localhost"))
         self.assertEquals(r.status_code, requests.codes.unauthorized)
 
+    def test_index_html(self):
+        r = requests.get(self.url("/"), auth=('admin', self.server_web_password))
+        self.assertEquals(r.status_code, requests.codes.ok)
+
     def test_split_request(self):
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
index bc24d08c5b8f5a04295b87546ab64e19000bf9c7..6b30359e3861c486bd029d9c1d4d59bab05949f3 100644 (file)
@@ -1,3 +1,5 @@
+import requests
+import unittest
 from test_helper import ApiTestCase, is_auth, is_recursor
 
 
@@ -68,3 +70,9 @@ class Servers(ApiTestCase):
         r = self.session.get(self.url("/api/v1/servers/localhost/statistics?statistic=uptimeAAAA"))
         self.assertEquals(r.status_code, 422)
         self.assertIn("Unknown statistic name", r.json()['error'])
+
+    @unittest.skipIf(is_auth(), "Not applicable")
+    def test_read_statistics_using_password(self):
+        r = requests.get(self.url("/api/v1/servers/localhost/statistics"), auth=('admin', self.server_web_password))
+        self.assertEquals(r.status_code, requests.codes.ok)
+        self.assert_success_json(r)
index ff756bad0a33c776e90192ea14a9779c36386803..514cea8e0ee2b72ad1e47d5b0ab7a5a3815aa468 100644 (file)
@@ -1801,7 +1801,7 @@ $ORIGIN %NAME%
         self.assertIsNone(dbrecs[0]['ordername'])
 
     def test_cname_at_ent_place(self):
-        name, payload, zone = self.create_zone(api_rectify=True)
+        name, payload, zone = self.create_zone(dnssec=True, api_rectify=True)
         rrset = {
             'changetype': 'replace',
             'name': 'sub2.sub1.' + name,
index 9ef00b919c76defa3bf82b843c0140858a3f3d2c..9a6ee028207492a990e26585bf6a28772c55cb5e 100644 (file)
@@ -25,6 +25,7 @@ class ApiTestCase(unittest.TestCase):
         self.server_address = '127.0.0.1'
         self.server_port = int(os.environ.get('WEBPORT', '5580'))
         self.server_url = 'http://%s:%s/' % (self.server_address, self.server_port)
+        self.server_web_password = os.environ.get('WEBPASSWORD', 'MISSING')
         self.session = requests.Session()
         self.session.headers = {'X-API-Key': os.environ.get('APIKEY', 'changeme-key'), 'Origin': 'http://%s:%s' % (self.server_address, self.server_port)}
 
index d04eb3dc4a67ffd2d0fc5cac3fd14fadef346831..9737ef868034b88bc49eb929bbe51b8dc0aa386f 100644 (file)
@@ -23,6 +23,25 @@ class AuthTest(unittest.TestCase):
     _confdir = 'auth'
     _authPort = 5300
 
+    _config_params = []
+
+    _config_template_default = """
+module-dir=../regression-tests/modules
+daemon=no
+local-ipv6=
+bind-config={confdir}/named.conf
+bind-dnssec-db={bind_dnssec_db}
+socket-dir={confdir}
+cache-ttl=0
+negquery-cache-ttl=0
+query-cache-ttl=0
+log-dns-queries=yes
+log-dns-details=yes
+loglevel=9
+distributor-threads=1"""
+
+    _config_template = ""
+
     _root_DS = "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
 
     # The default SOA for zones in the authoritative servers
@@ -92,28 +111,13 @@ options {
     def generateAuthConfig(cls, confdir):
         bind_dnssec_db = os.path.join(confdir, 'bind-dnssec.sqlite3')
 
+        params = tuple([getattr(cls, param) for param in cls._config_params])
+
         with open(os.path.join(confdir, 'pdns.conf'), 'w') as pdnsconf:
-            pdnsconf.write("""
-module-dir=../regression-tests/modules
-launch=bind geoip
-daemon=no
-local-ipv6=
-bind-config={confdir}/named.conf
-bind-dnssec-db={bind_dnssec_db}
-socket-dir={confdir}
-cache-ttl=0
-negquery-cache-ttl=0
-query-cache-ttl=0
-log-dns-queries=yes
-log-dns-details=yes
-loglevel=9
-geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb
-edns-subnet-processing=yes
-expand-alias=yes
-resolver={prefix}.1:5301
-any-to-tcp=no
-distributor-threads=1""".format(confdir=confdir, prefix=cls._PREFIX,
-                                bind_dnssec_db=bind_dnssec_db))
+            pdnsconf.write(cls._config_template_default.format(
+                confdir=confdir, prefix=cls._PREFIX,
+                bind_dnssec_db=bind_dnssec_db))
+            pdnsconf.write(cls._config_template % params)
 
         pdnsutilCmd = [os.environ['PDNSUTIL'],
                        '--config-dir=%s' % confdir,
index 1178534ee3c8e8fcd5cbf9135804a8fbc687a9e3..789b387473e914721288cc68c64cc65b9c8df489 100644 (file)
@@ -15,6 +15,15 @@ aliasUDPReactorRunning = False
 
 
 class TestALIAS(AuthTest):
+    _config_template = """
+expand-alias=yes
+resolver=%s.1:5301
+any-to-tcp=no
+launch=bind
+"""
+
+    _config_params = ['_PREFIX']
+
     _zones = {
         'example.org': """
 example.org.                 3600 IN SOA  {soa}
index ffe762af9798b7a191f4c2668808c2eb67c9d613..6ebf4427b361674252bf782b691712fc12eef87d 100644 (file)
@@ -30,6 +30,13 @@ class FakeHTTPServer(BaseHTTPRequestHandler):
         self._set_headers()
 
 class TestLuaRecords(AuthTest):
+    _config_template = """
+geoip-database-files=../modules/geoipbackend/regression-tests/GeoLiteCity.mmdb
+edns-subnet-processing=yes
+launch=bind geoip
+any-to-tcp=no
+"""
+
     _zones = {
         'example.org': """
 example.org.                 3600 IN SOA  {soa}
index fa5d736985504707f2f255e5c0187b6e641fe5d2..ddb427ce01c301b3e189d836681b0fc08d7e59f1 100644 (file)
@@ -18,3 +18,6 @@ countryName = NL
 [custom_extensions]
 basicConstraints = CA:true
 keyUsage = cRLSign, keyCertSign
+
+[CA_default]
+copy_extensions = copy
index 030cd5959f4215ac57518a549a2b3e79be83a19f..f1aa4c7feddbb3ff07fe529033afbf40108b29ff 100644 (file)
@@ -3,9 +3,18 @@ default_bits = 2048
 encrypt_key = no
 prompt = no
 distinguished_name = server_distinguished_name
+req_extensions = v3_req
 
 [server_distinguished_name]
 CN = tls.tests.dnsdist.org
 OU = PowerDNS.com BV
 countryName = NL
 
+[v3_req]
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = @alt_names
+
+[alt_names]
+DNS.1 = tls.tests.dnsdist.org
+DNS.2 = powerdns.com
index 251e76a6a79d99f39591f580c8792adbb3268dd1..1f6de2ea1c0f1504063781cec1186d8c742814a9 100755 (executable)
@@ -54,7 +54,7 @@ openssl req -new -x509 -days 1 -extensions v3_ca -keyout ca.key -out ca.pem -nod
 # Generate a new server certificate request
 openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr -config configServer.conf
 # Sign the server cert
-openssl x509 -req -days 1 -CA ca.pem -CAkey ca.key -CAcreateserial -in server.csr -out server.pem
+openssl x509 -req -days 1 -CA ca.pem -CAkey ca.key -CAcreateserial -in server.csr -out server.pem -extfile configServer.conf -extensions v3_req
 # Generate a chain
 cat server.pem ca.pem > server.chain
 
index 8ec87804a7f352e00a4a611830e668d210db6149..178e79083d8652b273520c509d1018122c0fa25d 100644 (file)
@@ -57,6 +57,7 @@ class TestAPIBasics(DNSDistTest):
             url = 'http://127.0.0.1:' + str(self._webServerPort) + path
             r = requests.get(url, headers=headers, timeout=self._webTimeout)
             self.assertEquals(r.status_code, 401)
+
     def testBasicAuthOnly(self):
         """
         API: Basic Authentication Only
@@ -230,8 +231,8 @@ class TestAPIBasics(DNSDistTest):
                     'rule-drop', 'rule-nxdomain', 'rule-refused', 'self-answered', 'downstream-timeouts',
                     'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
                     'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
-                    'latency-slow', 'latency-avg100', 'latency-avg1000', 'latency-avg10000',
-                    'latency-avg1000000', 'uptime', 'real-memory-usage', 'noncompliant-queries',
+                    'latency-slow', 'latency-sum', 'latency-count', 'latency-avg100', 'latency-avg1000',
+                    'latency-avg10000', 'latency-avg1000000', 'uptime', 'real-memory-usage', 'noncompliant-queries',
                     'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
                     'cache-misses', 'cpu-user-msec', 'cpu-sys-msec', 'fd-usage', 'dyn-blocked',
                     'dyn-block-nmg-size', 'rule-servfail', 'security-status']
index f730821abaa00fe6bf967811a13728a1354a43fe..4943c96ec11443eb2e2b6befc8ea0843af571c4f 100644 (file)
@@ -12,8 +12,11 @@ import pycurl
 class DNSDistDOHTest(DNSDistTest):
 
     @classmethod
-    def getDOHGetURL(cls, baseurl, query):
-        wire = query.to_wire()
+    def getDOHGetURL(cls, baseurl, query, rawQuery=False):
+        if rawQuery:
+            wire = query
+        else:
+            wire = query.to_wire()
         param = base64.urlsafe_b64encode(wire).decode('UTF8').rstrip('=')
         return baseurl + "?dns=" + param
 
@@ -27,14 +30,49 @@ class DNSDistDOHTest(DNSDistTest):
         return conn
 
     @classmethod
-    def sendDOHQuery(cls, port, servername, baseurl, query, response=None, timeout=2.0, caFile=None, useQueue=True):
-        url = cls.getDOHGetURL(baseurl, query)
+    def sendDOHQuery(cls, port, servername, baseurl, query, response=None, timeout=2.0, caFile=None, useQueue=True, rawQuery=False, customHeaders=[]):
+        url = cls.getDOHGetURL(baseurl, query, rawQuery)
         conn = cls.openDOHConnection(port, caFile=caFile, timeout=timeout)
         #conn.setopt(pycurl.VERBOSE, True)
         conn.setopt(pycurl.URL, url)
         conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (servername, port)])
         conn.setopt(pycurl.SSL_VERIFYPEER, 1)
         conn.setopt(pycurl.SSL_VERIFYHOST, 2)
+        conn.setopt(pycurl.HTTPHEADER, customHeaders)
+        if caFile:
+            conn.setopt(pycurl.CAINFO, caFile)
+
+        if response:
+            cls._toResponderQueue.put(response, True, timeout)
+
+        receivedQuery = None
+        message = None
+        data = conn.perform_rb()
+        rcode = conn.getinfo(pycurl.RESPONSE_CODE)
+        if rcode == 200:
+            message = dns.message.from_wire(data)
+
+        if useQueue and not cls._fromResponderQueue.empty():
+            receivedQuery = cls._fromResponderQueue.get(True, timeout)
+
+        return (receivedQuery, message)
+
+    @classmethod
+    def sendDOHPostQuery(cls, port, servername, baseurl, query, response=None, timeout=2.0, caFile=None, useQueue=True, rawQuery=False):
+        url = baseurl
+        conn = cls.openDOHConnection(port, caFile=caFile, timeout=timeout)
+        #conn.setopt(pycurl.VERBOSE, True)
+        conn.setopt(pycurl.URL, url)
+        conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (servername, port)])
+        conn.setopt(pycurl.SSL_VERIFYPEER, 1)
+        conn.setopt(pycurl.SSL_VERIFYHOST, 2)
+        conn.setopt(pycurl.POST, True)
+        data = query
+        if not rawQuery:
+            data = data.to_wire()
+
+        conn.setopt(pycurl.POSTFIELDS, data)
+
         if caFile:
             conn.setopt(pycurl.CAINFO, caFile)
 
@@ -99,6 +137,8 @@ class TestDOH(DNSDistDOHTest):
     addAction("drop.doh.tests.powerdns.com.", DropAction())
     addAction("refused.doh.tests.powerdns.com.", RCodeAction(DNSRCode.REFUSED))
     addAction("spoof.doh.tests.powerdns.com.", SpoofAction("1.2.3.4"))
+    addAction(HTTPHeaderRule("X-PowerDNS", "^[a]{5}$"), SpoofAction("2.3.4.5"))
+    addAction(HTTPPathRule("/PowerDNS"), SpoofAction("3.4.5.6"))
     """
     _config_params = ['_testServerPort', '_dohServerPort', '_serverCert', '_serverKey']
 
@@ -127,6 +167,31 @@ class TestDOH(DNSDistDOHTest):
         self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
         self.assertEquals(response, receivedResponse)
 
+    def testDOHSimplePOST(self):
+        """
+        DOH: Simple POST query
+        """
+        name = 'simple-post.doh.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+        query.id = 0
+        expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+        expectedQuery.id = 0
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendDOHPostQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = expectedQuery.id
+        self.assertEquals(expectedQuery, receivedQuery)
+        self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
     def testDOHExistingEDNS(self):
         """
         DOH: Existing EDNS
@@ -185,7 +250,6 @@ class TestDOH(DNSDistDOHTest):
         name = 'drop.doh.tests.powerdns.com.'
         query = dns.message.make_query(name, 'A', 'IN')
         (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, caFile=self._caCert, query=query, response=None, useQueue=False)
-        print(receivedResponse)
         self.assertEquals(receivedResponse, None)
 
     def testRefused(self):
@@ -220,6 +284,163 @@ class TestDOH(DNSDistDOHTest):
         (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, caFile=self._caCert, query=query, response=None, useQueue=False)
         self.assertEquals(receivedResponse, expectedResponse)
 
+    def testDOHInvalid(self):
+        """
+        DOH: Invalid query
+        """
+        name = 'invalid.doh.tests.powerdns.com.'
+        invalidQuery = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+        invalidQuery.id = 0
+        # first an invalid query
+        invalidQuery = invalidQuery.to_wire()
+        invalidQuery = invalidQuery[:-5]
+        (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, caFile=self._caCert, query=invalidQuery, response=None, useQueue=False, rawQuery=True)
+        self.assertEquals(receivedResponse, None)
+
+        # and now a valid one
+        query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+        query.id = 0
+        expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+        expectedQuery.id = 0
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+        (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = expectedQuery.id
+        self.assertEquals(expectedQuery, receivedQuery)
+        self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+    def testDOHWithoutQuery(self):
+        """
+        DOH: Empty GET query
+        """
+        name = 'empty-get.doh.tests.powerdns.com.'
+        url = self._dohBaseURL
+        conn = self.openDOHConnection(self._dohServerPort, self._caCert, timeout=2.0)
+        conn.setopt(pycurl.URL, url)
+        conn.setopt(pycurl.RESOLVE, ["%s:%d:127.0.0.1" % (self._serverName, self._dohServerPort)])
+        conn.setopt(pycurl.SSL_VERIFYPEER, 1)
+        conn.setopt(pycurl.SSL_VERIFYHOST, 2)
+        conn.setopt(pycurl.CAINFO, self._caCert)
+        data = conn.perform_rb()
+        rcode = conn.getinfo(pycurl.RESPONSE_CODE)
+        self.assertEquals(rcode, 400)
+
+    def testDOHEmptyPOST(self):
+        """
+        DOH: Empty POST query
+        """
+        name = 'empty-post.doh.tests.powerdns.com.'
+
+        (_, receivedResponse) = self.sendDOHPostQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query="", rawQuery=True, response=None, caFile=self._caCert)
+        self.assertEquals(receivedResponse, None)
+
+        # and now a valid one
+        query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+        query.id = 0
+        expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+        expectedQuery.id = 0
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+        (receivedQuery, receivedResponse) = self.sendDOHPostQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = expectedQuery.id
+        self.assertEquals(expectedQuery, receivedQuery)
+        self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+    def testHeaderRule(self):
+        """
+        DOH: HeaderRule
+        """
+        name = 'header-rule.doh.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        query.id = 0
+        query.flags &= ~dns.flags.RD
+        expectedResponse = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '2.3.4.5')
+        expectedResponse.answer.append(rrset)
+
+        # this header should match
+        (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, caFile=self._caCert, query=query, response=None, useQueue=False, customHeaders=['x-powerdnS: aaaaa'])
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+        expectedQuery.flags &= ~dns.flags.RD
+        expectedQuery.id = 0
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+
+        # this content of the header should NOT match
+        (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL, query, response=response, caFile=self._caCert, customHeaders=['x-powerdnS: bbbbb'])
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = expectedQuery.id
+        self.assertEquals(expectedQuery, receivedQuery)
+        self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+    def testHTTPPath(self):
+        """
+        DOH: HTTPPath
+        """
+        name = 'http-path.doh.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        query.id = 0
+        query.flags &= ~dns.flags.RD
+        expectedResponse = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '3.4.5.6')
+        expectedResponse.answer.append(rrset)
+
+        # this path should match
+        (_, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + 'PowerDNS', caFile=self._caCert, query=query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        expectedQuery = dns.message.make_query(name, 'A', 'IN', use_edns=True, payload=4096)
+        expectedQuery.id = 0
+        expectedQuery.flags &= ~dns.flags.RD
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+
+        # this path should NOT match
+        (receivedQuery, receivedResponse) = self.sendDOHQuery(self._dohServerPort, self._serverName, self._dohBaseURL + "PowerDNS2", query, response=response, caFile=self._caCert)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = expectedQuery.id
+        self.assertEquals(expectedQuery, receivedQuery)
+        self.checkQueryEDNSWithoutECS(expectedQuery, receivedQuery)
+        self.assertEquals(response, receivedResponse)
 
 class TestDOHAddingECS(DNSDistDOHTest):
 
index b31c6c2f9bfcf4933bdc99103b5eb2febbf2a035..6973613b45e0f6f90394a9ca3351bf6a12233810 100644 (file)
@@ -12,6 +12,7 @@ class TestTLS(DNSDistTest):
     _config_template = """
     newServer{address="127.0.0.1:%s"}
     addTLSLocal("127.0.0.1:%s", "%s", "%s")
+    addAction(SNIRule("powerdns.com"), SpoofAction("1.2.3.4"))
     """
     _config_params = ['_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey']
 
@@ -90,3 +91,44 @@ class TestTLS(DNSDistTest):
             receivedQuery.id = query.id
             self.assertEquals(query, receivedQuery)
             self.assertEquals(response, receivedResponse)
+
+    def testTLSSNIRouting(self):
+        """
+        TLS: SNI Routing
+        """
+        name = 'sni.tls.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN', use_edns=False)
+        query.flags &= ~dns.flags.RD
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '127.0.0.1')
+        response.answer.append(rrset)
+        expectedResponse = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '1.2.3.4')
+        expectedResponse.answer.append(rrset)
+
+        # this SNI should match so we should get a spoofed answer
+        conn = self.openTLSConnection(self._tlsServerPort, 'powerdns.com', self._caCert)
+
+        self.sendTCPQueryOverConnection(conn, query, response=None)
+        receivedResponse = self.recvTCPResponseOverConnection(conn, useQueue=False)
+        self.assertTrue(receivedResponse)
+        self.assertEquals(expectedResponse, receivedResponse)
+
+        # this one should not
+        conn = self.openTLSConnection(self._tlsServerPort, self._serverName, self._caCert)
+
+        self.sendTCPQueryOverConnection(conn, query, response=response)
+        (receivedQuery, receivedResponse) = self.recvTCPResponseOverConnection(conn, useQueue=True)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
index a433d60d22927fbbbd3296fd4aff2d1d2dc80ff8..88cd97cf8c63287b06a4ad908ce006a6a022ef79 100755 (executable)
@@ -37,8 +37,13 @@ faketime -m -f -$delta $PDNS --config-dir=soa-edit &
 bindwait
 
 $SDIG 127.0.0.1 $port minimal.com SOA | LC_ALL=C sort
+$SDIG 127.0.0.1 $port minimal.com MX | LC_ALL=C sort
+$SDIG 127.0.0.1 $port nx.minimal.com MX | LC_ALL=C sort
 sleep 15
+echo midnight has passed
 $SDIG 127.0.0.1 $port minimal.com SOA | LC_ALL=C sort
-       
+$SDIG 127.0.0.1 $port minimal.com MX | LC_ALL=C sort
+$SDIG 127.0.0.1 $port nx.minimal.com MX | LC_ALL=C sort
+
 kill $(cat pdns*.pid)
 rm pdns*.pid
index 44de3d57e81e0017eff6077686b0e7bc98fb2392..7f9d928e23af1f8051c491436a87a34bc73ba9a2 100644 (file)
@@ -2,6 +2,19 @@ Set 'minimal.com' meta SOA-EDIT = INCREMENT-WEEKS
 0      minimal.com.    IN      SOA     120     ns1.example.com. ahu.example.com. 2000083846 28800 7200 604800 86400
 Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
 Reply to question for qname='minimal.com.', qtype=SOA
+1      minimal.com.    IN      SOA     120     ns1.example.com. ahu.example.com. 2000083846 28800 7200 604800 86400
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='minimal.com.', qtype=MX
+1      minimal.com.    IN      SOA     120     ns1.example.com. ahu.example.com. 2000083846 28800 7200 604800 86400
+Rcode: 3 (Non-Existent domain), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='nx.minimal.com.', qtype=MX
+midnight has passed
 0      minimal.com.    IN      SOA     120     ns1.example.com. ahu.example.com. 2000083847 28800 7200 604800 86400
 Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
 Reply to question for qname='minimal.com.', qtype=SOA
+1      minimal.com.    IN      SOA     120     ns1.example.com. ahu.example.com. 2000083847 28800 7200 604800 86400
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='minimal.com.', qtype=MX
+1      minimal.com.    IN      SOA     120     ns1.example.com. ahu.example.com. 2000083847 28800 7200 604800 86400
+Rcode: 3 (Non-Existent domain), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='nx.minimal.com.', qtype=MX
index 50923a84ce3bb00e2e52da391a53f871fcd98890..4f1a674a3cea9cb6211e35e3eb8a3df7f665db4f 100755 (executable)
@@ -11,6 +11,8 @@ pip install -U pip
 pip install -r requirements.txt
 
 protoc -I=../pdns/ --python_out=. ../pdns/dnsmessage.proto
+protoc -I=../pdns/ --python_out=. ../pdns/dnstap.proto
+
 
 mkdir -p configs
 
@@ -28,6 +30,11 @@ if [ $(uname -s) = "Darwin" ]; then
   LIBFAKETIME_DEFAULT=/usr/local/lib/faketime/libfaketime.1.dylib
   LIBAUTHBIND_DEFAULT=""
 fi
+if [ $(uname -s) = "OpenBSD" ]; then
+  # OpenBSD is not /really/ supported here; it works for some tests, and then you might need sudo.
+  LIBFAKETIME_DEFAULT=""
+  LIBAUTHBIND_DEFAULT=""
+fi
 
 export LIBFAKETIME=${LIBFAKETIME:-$LIBFAKETIME_DEFAULT}
 export LIBAUTHBIND=${LIBAUTHBIND:-$LIBAUTHBIND_DEFAULT}
@@ -45,4 +52,13 @@ set -e
 if [ "${PDNS_DEBUG}" = "YES" ]; then
   set -x
 fi
+
+if ! "$PDNSRECURSOR" --version 2>&1 | grep Features | grep -q dnstap-framestream; then
+  export NODNSTAPTESTS=1
+fi
+
+if [ "${LIBAUTHBIND}" != "" -o "${LIBFAKETIME}" != "" ]; then
 LD_PRELOAD="${LIBASAN} ${LIBAUTHBIND} ${LIBFAKETIME}" nosetests -I test_WellKnown.py --with-xunit $@
+else
+nosetests -I test_WellKnown.py --with-xunit $@
+fi
diff --git a/regression-tests.recursor-dnssec/test_RecDnstap.py b/regression-tests.recursor-dnssec/test_RecDnstap.py
new file mode 100644 (file)
index 0000000..c94c1b5
--- /dev/null
@@ -0,0 +1,308 @@
+import dns
+import dnsmessage_pb2
+import os
+import socket
+import struct
+import sys
+import threading
+import time
+
+import dns
+import dnstap_pb2
+
+FSTRM_CONTROL_ACCEPT = 0x01
+FSTRM_CONTROL_START = 0x02
+FSTRM_CONTROL_STOP = 0x03
+FSTRM_CONTROL_READY = 0x04
+FSTRM_CONTROL_FINISH = 0x05
+
+# Python2/3 compatibility hacks
+try:
+  from queue import Queue
+except ImportError:
+  from Queue import Queue
+
+try:
+  range = xrange
+except NameError:
+  pass
+
+from nose import SkipTest
+from recursortests import RecursorTest
+
+def checkDnstapBase(testinstance, dnstap, protocol, initiator):
+    testinstance.assertTrue(dnstap)
+    testinstance.assertTrue(dnstap.HasField('identity'))
+    #testinstance.assertEqual(dnstap.identity, b'a.server')
+    testinstance.assertTrue(dnstap.HasField('version'))
+    #testinstance.assertIn(b'dnsdist ', dnstap.version)
+    testinstance.assertTrue(dnstap.HasField('type'))
+    testinstance.assertEqual(dnstap.type, dnstap.MESSAGE)
+    testinstance.assertTrue(dnstap.HasField('message'))
+    testinstance.assertTrue(dnstap.message.HasField('socket_protocol'))
+    testinstance.assertEqual(dnstap.message.socket_protocol, protocol)
+    testinstance.assertTrue(dnstap.message.HasField('socket_family'))
+    testinstance.assertEquals(dnstap.message.socket_family, dnstap_pb2.INET)
+    #
+    # We cannot check the query address and port since we only log outgoing queries via dnstap
+    #
+    #testinstance.assertTrue(dnstap.message.HasField('query_address'))
+    #testinstance.assertEquals(socket.inet_ntop(socket.AF_INET, dnstap.message.query_address), initiator)
+    testinstance.assertTrue(dnstap.message.HasField('response_address'))
+    testinstance.assertEquals(socket.inet_ntop(socket.AF_INET, dnstap.message.response_address), initiator)
+    testinstance.assertTrue(dnstap.message.HasField('response_port'))
+    testinstance.assertEquals(dnstap.message.response_port, 53)
+
+
+def checkDnstapQuery(testinstance, dnstap, protocol, query, initiator='127.0.0.1'):
+    testinstance.assertEquals(dnstap.message.type, dnstap_pb2.Message.RESOLVER_QUERY)
+    checkDnstapBase(testinstance, dnstap, protocol, initiator)
+
+    testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
+    testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
+
+    testinstance.assertTrue(dnstap.message.HasField('query_message'))
+    #
+    # We cannot compare the incoming query with the outgoing one
+    # The IDs and some other fields will be different
+    #
+    wire_message = dns.message.from_wire(dnstap.message.query_message)
+    #testinstance.assertEqual(wire_message, query)
+
+
+def checkDnstapExtra(testinstance, dnstap, expected):
+    testinstance.assertTrue(dnstap.HasField('extra'))
+    testinstance.assertEqual(dnstap.extra, expected)
+
+
+def checkDnstapNoExtra(testinstance, dnstap):
+    testinstance.assertFalse(dnstap.HasField('extra'))
+
+
+def checkDnstapResponse(testinstance, dnstap, protocol, response, initiator='127.0.0.1'):
+    testinstance.assertEquals(dnstap.message.type, dnstap_pb2.Message.RESOLVER_RESPONSE)
+    checkDnstapBase(testinstance, dnstap, protocol, initiator)
+
+    testinstance.assertTrue(dnstap.message.HasField('query_time_sec'))
+    testinstance.assertTrue(dnstap.message.HasField('query_time_nsec'))
+
+    testinstance.assertTrue(dnstap.message.HasField('response_time_sec'))
+    testinstance.assertTrue(dnstap.message.HasField('response_time_nsec'))
+
+    testinstance.assertTrue(dnstap.message.response_time_sec > dnstap.message.query_time_sec or \
+                            dnstap.message.response_time_nsec > dnstap.message.query_time_nsec)
+
+    testinstance.assertTrue(dnstap.message.HasField('response_message'))
+    wire_message = dns.message.from_wire(dnstap.message.response_message)
+    testinstance.assertEqual(wire_message, response)
+
+def fstrm_get_control_frame_type(data):
+    (t,) = struct.unpack("!L", data[0:4])
+    return t
+
+
+def fstrm_make_control_frame_reply(cft, data):
+    if cft == FSTRM_CONTROL_READY:
+        # Reply with ACCEPT frame and content-type
+        contenttype = b'protobuf:dnstap.Dnstap'
+        frame = struct.pack('!LLL', FSTRM_CONTROL_ACCEPT, 1,
+                            len(contenttype)) + contenttype
+        buf = struct.pack("!LL", 0, len(frame)) + frame
+        return buf
+    elif cft == FSTRM_CONTROL_START:
+        return None
+    else:
+        raise Exception('unhandled control frame ' + cft)
+
+
+def fstrm_read_and_dispatch_control_frame(conn):
+    data = conn.recv(4)
+    if not data:
+        raise Exception('length of control frame payload could not be read')
+    (datalen,) = struct.unpack("!L", data)
+    data = conn.recv(datalen)
+    cft = fstrm_get_control_frame_type(data)
+    reply = fstrm_make_control_frame_reply(cft, data)
+    if reply:
+        conn.send(reply)
+    return cft
+
+
+def fstrm_handle_bidir_connection(conn, on_data):
+    data = None
+    while True:
+        data = conn.recv(4)
+        if not data:
+            break
+        (datalen,) = struct.unpack("!L", data)
+        if datalen == 0:
+            # control frame length follows
+            cft = fstrm_read_and_dispatch_control_frame(conn)
+            if cft == FSTRM_CONTROL_STOP:
+                break
+        else:
+            # data frame
+            data = conn.recv(datalen)
+            if not data:
+                break
+
+            on_data(data)
+
+
+
+class DNSTapServerParams:
+  def __init__(self, port):
+    self.queue = Queue()
+    self.port = port
+
+
+DNSTapServerParameters = DNSTapServerParams(4243)
+DNSTapListeners = []
+
+class TestRecursorDNSTap(RecursorTest):
+    @classmethod
+    def FrameStreamUnixListener(cls, conn, param):
+        while True:
+            try:
+                fstrm_handle_bidir_connection(conn, lambda data: \
+                param.queue.put(data, True, timeout=2.0))
+            except socket.error as e:
+                if e.errno == 9:
+                    break
+                printf("Unexpected socket error %d", e)
+                sys.exit(1)
+            conn.close()
+
+    @classmethod
+    def FrameStreamUnixListenerMain(cls, param):
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+        try:
+            sock.bind(("127.0.0.1", param.port))
+        except socket.error as e:
+            print("Error binding in the framestream listener: %s" % str(e))
+            sys.exit(1)
+        DNSTapListeners.append(sock)
+        sock.listen(100)
+        while True:
+            (conn, _) = sock.accept()
+            print("Accepting connection")
+            listener = threading.Thread(name='DNSTap Worker', target=cls.FrameStreamUnixListener, args=[conn, param])
+            listener.setDaemon(True)
+            listener.start()
+
+        sock.close()
+
+    @classmethod
+    def setUpClass(cls):
+       
+       if os.environ.get("NODNSTAPTESTS") == "1":
+               raise SkipTest("Not Yet Supported")
+
+        cls.setUpSockets()
+
+        cls.startResponders()
+
+        listener = threading.Thread(name='DNSTap Listener', target=cls.FrameStreamUnixListenerMain, args=[DNSTapServerParameters])
+        listener.setDaemon(True)
+        listener.start()
+
+
+        confdir = os.path.join('configs', cls._confdir)
+        cls.createConfigDir(confdir)
+
+        cls.generateRecursorConfig(confdir)
+        cls.startRecursor(confdir, cls._recursorPort)
+
+    def setUp(self):
+        # Make sure the queue is empty, in case
+        # a previous test failed
+        while not DNSTapServerParameters.queue.empty():
+            DNSTapServerParameters.queue.get(False)
+
+    @classmethod
+    def generateRecursorConfig(cls, confdir):
+        authzonepath = os.path.join(confdir, 'example.zone')
+        with open(authzonepath, 'w') as authzone:
+            authzone.write("""$ORIGIN example.
+@ 3600 IN SOA {soa}
+a 3600 IN A 192.0.2.42
+tagged 3600 IN A 192.0.2.84
+query-selected 3600 IN A 192.0.2.84
+answer-selected 3600 IN A 192.0.2.84
+types 3600 IN A 192.0.2.84
+types 3600 IN AAAA 2001:DB8::1
+types 3600 IN TXT "Lorem ipsum dolor sit amet"
+types 3600 IN MX 10 a.example.
+types 3600 IN SPF "v=spf1 -all"
+types 3600 IN SRV 10 20 443 a.example.
+cname 3600 IN CNAME a.example.
+
+""".format(soa=cls._SOA))
+        super(TestRecursorDNSTap, cls).generateRecursorConfig(confdir)
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.tearDownRecursor()
+        for listerner in DNSTapListeners:
+            listerner.close()
+
+class DNSTapDefaultTest(TestRecursorDNSTap):
+    """
+    This test makes sure that we correctly export outgoing queries over DNSTap.
+    It must be improved and setup env so we can check for incoming responses, but makes sure for now
+    that the recursor at least connects to the DNSTap server.
+    """
+
+    _confdir = 'DNSTapDefault'
+    _config_template = """
+auth-zones=example=configs/%s/example.zone""" % _confdir
+    _lua_config_file = """
+    dnstapFrameStreamServer({"127.0.0.1:%d"})
+    """ % (DNSTapServerParameters.port)
+
+    def getFirstDnstap(self):
+        data = DNSTapServerParameters.queue.get(True, timeout=2.0)
+        self.assertTrue(data)
+        dnstap = dnstap_pb2.Dnstap()
+        dnstap.ParseFromString(data)
+        return dnstap
+
+    def testA(self):
+
+        name = 'www.example.org.'
+        query = dns.message.make_query(name, 'A', want_dnssec=True)
+        query.flags |= dns.flags.RD
+        res = self.sendUDPQuery(query)
+
+        # check the DNSTap messages corresponding to the UDP query and answer
+        # check the dnstap message corresponding to the UDP query
+        dnstap = self.getFirstDnstap()
+
+        checkDnstapQuery(self, dnstap, dnstap_pb2.UDP, query, '127.0.0.8')
+        # We don't expect a response
+        checkDnstapNoExtra(self, dnstap)
+
+class DNSTapLogNoQueriesTest(TestRecursorDNSTap):
+    """
+    This test makes sure that we correctly export outgoing queries over DNSTap.
+    It must be improved and setup env so we can check for incoming responses, but makes sure for now
+    that the recursor at least connects to the DNSTap server.
+    """
+
+    _confdir = 'DNSTapLogNoQueries'
+    _config_template = """
+auth-zones=example=configs/%s/example.zone""" % _confdir
+    _lua_config_file = """
+    dnstapFrameStreamServer({"127.0.0.1:%d"}, {logQueries=false})
+    """ % (DNSTapServerParameters.port)
+
+    def testA(self):
+        name = 'www.example.org.'
+        query = dns.message.make_query(name, 'A', want_dnssec=True)
+        query.flags |= dns.flags.RD
+        res = self.sendUDPQuery(query)
+
+        # We don't expect anything
+        self.assertTrue(DNSTapServerParameters.queue.empty())
+
index 3586c5cca0b75b53251b3c3764ffcebd069077ca..896cba9125b3d4f8a026c7ec2eb2c4d062e9e80e 100644 (file)
@@ -152,3 +152,19 @@ This does not install the jdnssec-verifyzone tools. The test that will break wit
 touch tests/verify-dnssec-zone/allow-missing
 ```
 
+Getting required daemons from Docker
+------------------------------------
+
+Please keep in mind that databases may need a few seconds to start up.
+
+'MySQL':
+```sh
+docker run -p 3306:3306 --rm -d -e MYSQL_ALLOW_EMPTY_PASSWORD=1 mariadb
+GMYSQLHOST=127.0.0.1 ./start-test-stop 5300 gmysql
+```
+
+Postgres:
+```sh
+docker run -p 5432:5432 --rm -d postgres
+GPGSQLUSER=postgres PGHOST=127.0.0.1  ./start-test-stop 5300 gpgsql
+```
index d20cca75cf38f1bc5a4b4b0c3de5441e69c80222..3060051fc264766ac4334ab3ca8a6e37550797b3 100755 (executable)
@@ -117,6 +117,11 @@ do
                                result="        Failed test $a"
                                echo $testname >> failed_tests
                                failed=$[$failed+1]
+                               if [ "$FIX_TESTS" == "YES" ]
+                               then
+                                       mv -f $a/real_result $expected
+                                       result="$result (fixed)"
+                               fi
                        else
                                result="        Expected failure for reason $reason, test passed $a"
                                echo $testname >> passed_tests
index 6f4fca1c47820d1e994dc74299a15bd80b4f9f72..aec0868634f8118efcf0cbca481bc17f43494ef4 100755 (executable)
@@ -17,6 +17,7 @@ export ZONE2LDAP=${ZONE2LDAP:-${PWD}/../pdns/zone2ldap}
 export PDNSUTIL=${PDNSUTIL:-${PWD}/../pdns/pdnsutil}
 export PDNSCONTROL=${PDNSCONTROL:-${PWD}/../pdns/pdns_control}
 export RESOLVERIP=${RESOLVERIP:-8.8.8.8}
+export FIX_TESTS=${FIX_TESTS:-NO}
 
 
 ALGORITHM=${ALGORITHM:="hmac-md5"}
index 32ef5676e0f3201472bf25a3beec41799ee5c1ba..39f77560b94d4ae9b4bb13e2b8bd6982994316c5 100755 (executable)
@@ -19,6 +19,15 @@ echo '* check that indeed nothing was added'
 cleandig $RECORDNAME A hidesoadetails
 cleandig $RECORDNAME CNAME hidesoadetails
 
+echo '* Add a record that will result in an ENT being inserted at the upcoming CNAME'
+cleannsupdate <<!
+server $nameserver $port
+zone test.dyndns
+update add sub.$RECORDNAME. 3600 A 192.0.2.1
+send
+answer
+!
+
 echo '* Add a CNAME'
 cleannsupdate <<!
 server $nameserver $port
@@ -28,6 +37,15 @@ send
 answer
 !
 
+echo '* Delete the now useless record inserted to get an ENT there'
+cleannsupdate <<!
+server $nameserver $port
+zone test.dyndns
+update delete sub.$RECORDNAME. 3600 A 192.0.2.1
+send
+answer
+!
+
 echo '* Attempt to add an A record (should be REFUSED)'
 cleannsupdate <<!
 server $nameserver $port
index 6cee2c490079cb209499d8576ba1825fcaf1f9c8..371bdf9fdecf625e3b039fd9b98832b62bea7253 100644 (file)
@@ -16,6 +16,13 @@ Reply to question for qname='cname-and-other.test.dyndns.', qtype=A
 1      test.dyndns.    IN      SOA     3600    ns1.test.dyndns. ahu.example.dyndns. [serial] 28800 7200 604800 86400
 Rcode: 3 (Non-Existent domain), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
 Reply to question for qname='cname-and-other.test.dyndns.', qtype=CNAME
+* Add a record that will result in an ENT being inserted at the upcoming CNAME
+Answer:
+;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id: [id]
+;; flags: qr aa; ZONE: 1, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
+;; ZONE SECTION:
+;test.dyndns.                  IN      SOA
+
 * Add a CNAME
 Answer:
 ;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id: [id]
@@ -23,6 +30,13 @@ Answer:
 ;; ZONE SECTION:
 ;test.dyndns.                  IN      SOA
 
+* Delete the now useless record inserted to get an ENT there
+Answer:
+;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id: [id]
+;; flags: qr aa; ZONE: 1, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
+;; ZONE SECTION:
+;test.dyndns.                  IN      SOA
+
 * Attempt to add an A record (should be REFUSED)
 Answer:
 ;; ->>HEADER<<- opcode: UPDATE, status: REFUSED, id: [id]
similarity index 53%
rename from regression-tests/tests/too-big-for-udp-query-no-truncate-additional/description
rename to regression-tests/tests/too-big-for-udp-query-always-truncate-additional/description
index e694a2fcad1cc32234ce1aa3d62a1a88f208861d..b00e0ccc74e69e62af860617fa66e25387eff3d0 100644 (file)
@@ -1,4 +1,6 @@
 UDP dns packets can only be 512 bytes long - when they are longer, they need
 to get truncated, and have the 'TC' bit set, to inform the client that they
 need to requery over TCP. This query however does not need truncation, since
-the information that causes things to go over limit is 'additional'.
+the information that causes things to go over limit are 'courtesy' additionals.
+But truncation is harmless and will avoid a lot of additional complexity.
+https://tools.ietf.org/html/rfc4472#appendix-B
diff --git a/regression-tests/tests/too-big-for-udp-query-always-truncate-additional/expected_result b/regression-tests/tests/too-big-for-udp-query-always-truncate-additional/expected_result
new file mode 100644 (file)
index 0000000..d714295
--- /dev/null
@@ -0,0 +1,2 @@
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 1, AA: 1, opcode: 0
+Reply to question for qname='together-too-much.example.com.', qtype=MX
diff --git a/regression-tests/tests/too-big-for-udp-query-no-truncate-additional/expected_result b/regression-tests/tests/too-big-for-udp-query-no-truncate-additional/expected_result
deleted file mode 100644 (file)
index 99aa109..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-0      together-too-much.example.com.  IN      MX      120     25 toomuchinfo-X.example.com.
-0      together-too-much.example.com.  IN      MX      120     25 toomuchinfo-X.example.com.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-2      toomuchinfo-X.example.com.      IN      A       120     192.168.99.
-Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
-Reply to question for qname='together-too-much.example.com.', qtype=MX