Version in base suite: 2.89-1 Base version: dnsmasq_2.89-1 Target version: dnsmasq_2.90-4~deb12u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/d/dnsmasq/dnsmasq_2.89-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/d/dnsmasq/dnsmasq_2.90-4~deb12u1.dsc CHANGELOG | 67 Makefile | 7 VERSION | 2 dbus/DBus-interface | 8 debian/changelog | 487 ++-- debian/conffiles | 5 debian/control | 45 debian/copyright | 70 debian/default | 42 debian/dnsmasq-base-lua.dirs | 1 debian/dnsmasq-base-lua.docs | 8 debian/dnsmasq-base-lua.install | 3 debian/dnsmasq-base-lua.links | 2 debian/dnsmasq-base-lua.maintscript | 9 debian/dnsmasq-base-lua.postinst | 30 debian/dnsmasq-base-lua.postrm | 13 debian/dnsmasq-base.conffiles | 1 debian/dnsmasq-base.dirs | 1 debian/dnsmasq-base.docs | 8 debian/dnsmasq-base.install | 3 debian/dnsmasq-base.maintscript | 3 debian/dnsmasq-base.postinst | 18 debian/dnsmasq-base.postrm | 10 debian/dnsmasq-utils.install | 3 debian/dnsmasq-utils.manpages | 3 debian/dnsmasq.default | 42 debian/dnsmasq.init | 170 + debian/dnsmasq.install | 8 debian/dnsmasq.links | 1 debian/dnsmasq.maintscript | 2 debian/dnsmasq.runscript/run | 4 debian/dnsmasq.service | 31 debian/dnsmasq.tmpfiles | 1 debian/dnsmasq@.service | 31 debian/init | 325 -- debian/init-system-common | 102 debian/installed-marker | 2 debian/lintian-override | 3 debian/patches/eliminate-privacy-breaches.patch | 40 debian/patches/series | 1 debian/postinst | 41 debian/postrm | 25 debian/preinst | 5 debian/prerm | 14 debian/readme | 22 debian/resolvconf | 2 debian/rules | 334 --- debian/source/format | 2 debian/systemd-helper | 34 debian/systemd.service | 31 debian/systemd@.service | 31 debian/tests/compile-time-options | 7 debian/tests/compile-time-options+lua | 7 debian/tests/control | 39 debian/tests/functions | 151 + debian/tests/functions.d/add-to.interfaces | 18 debian/tests/functions.d/add-to.named.conf.local | 2 debian/tests/functions.d/db.autopkg.test | 18 debian/tests/functions.d/dig.patterns | 4 debian/tests/functions.d/dnsmasq-autopkgtest.conf | 6 debian/tests/functions.d/dnsmasq.alt-autopkgtest.default | 42 debian/tests/functions.d/ip-addr.patterns | 6 debian/tests/functions.d/log.lua | 40 debian/tests/functions.d/log.patterns | 10 debian/tests/functions.d/named.conf.options | 6 debian/tests/functions.d/options+lua.patterns | 1 debian/tests/functions.d/options.patterns | 1 debian/tests/get-address+query-dns+check-utils | 19 debian/tests/get-address+query-dns+lua+alt | 19 debian/tests/get-address+query-dns+sysv+alt | 18 debian/upstream/metadata | 9 debian/upstream/signing-key.asc | 63 debian/watch | 5 dnsmasq.conf.example | 2 man/dnsmasq.8 | 59 src/arp.c | 2 src/auth.c | 2 src/blockdata.c | 123 - src/bpf.c | 2 src/cache.c | 248 +- src/config.h | 11 src/conntrack.c | 2 src/crypto.c | 2 src/dbus.c | 73 src/dhcp-common.c | 12 src/dhcp-protocol.h | 2 src/dhcp.c | 10 src/dhcp6-protocol.h | 2 src/dhcp6.c | 40 src/dns-protocol.h | 9 src/dnsmasq.c | 143 - src/dnsmasq.h | 101 src/dnssec.c | 740 +++--- src/domain-match.c | 7 src/domain.c | 24 src/dump.c | 2 src/edns0.c | 21 src/forward.c | 466 ++-- src/hash-questions.c | 4 src/helper.c | 2 src/inotify.c | 4 src/ip6addr.h | 2 src/lease.c | 47 src/log.c | 2 src/loop.c | 4 src/metrics.c | 6 src/metrics.h | 6 src/netlink.c | 2 src/network.c | 72 src/nftset.c | 18 src/option.c | 552 +++-- src/outpacket.c | 2 src/pattern.c | 2 src/poll.c | 2 src/radv-protocol.h | 2 src/radv.c | 11 src/rfc1035.c | 1655 ++++++++------- src/rfc2131.c | 4 src/rfc3315.c | 66 src/rrfilter.c | 151 + src/slaac.c | 2 src/tftp.c | 12 src/ubus.c | 2 src/util.c | 19 124 files changed, 4486 insertions(+), 2914 deletions(-) diff -Nru dnsmasq-2.89/CHANGELOG dnsmasq-2.90/CHANGELOG --- dnsmasq-2.89/CHANGELOG 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/CHANGELOG 2024-02-13 13:49:15.000000000 +0000 @@ -1,3 +1,70 @@ +version 2.90 + Fix reversion in --rev-server introduced in 2.88 which + caused breakage if the prefix length is not exactly divisible + by 8 (IPv4) or 4 (IPv6). + + Fix possible SEGV when there server(s) for a particular + domain are configured, but no server which is not qualified + for a particular domain. Thanks to Daniel Danzberger for + spotting this bug. + + Set the default maximum DNS UDP packet sice to 1232. This + has been the recommended value since 2020 because it's the + largest value that avoid fragmentation, and fragmentation + is just not reliable on the modern internet, especially + for IPv6. It's still possible to override this with + --edns-packet-max for special circumstances. + + Add --no-dhcpv4-interface and --no-dhcpv6-interface for + better control over which inetrfaces are providing DHCP service. + + Fix issue with stale caching: After replying with stale data, + dnsmasq sends the query upstream to refresh the cache asynchronously + and sometimes sends the wrong packet: packet length can be wrong, + and if an EDE marking stale data is added to the answer that can + end up in the query also. This bug only seems to cause problems + when the usptream server is a DOH/DOT proxy. Thanks to Justin He + for the bug report. + + Add configurable caching for arbitrary RR-types. + + Add --filter-rr option, to filter arbitrary RR-types. + --filter-rr=ANY has a special meaning: it filters the + answers to queries for the ANY RR-type. + + Add limits on the resources used to do DNSSEC validation. + DNSSEC introduces a potential CPU DoS, because a crafted domain + can force a validator to a large number of cryptographic + operations whilst attempting to do validation. When using TCP + transport a DNSKEY RRset contain thousands of members and any + RRset can have thousands of signatures. The potential number + of signature validations to follow the RFC for validation + for one RRset is the cross product of the keys and signatures, + so millions. In practice, the actual numbers are much lower, + so attacks can be mitigated by limiting the amount of + cryptographic "work" to a much lower amount. The actual + limits are number a signature validation fails per RRset(20), + number of signature validations and hash computations + per query(200), number of sub-queries to fetch DS and DNSKEY + RRsets per query(40), and the number of iterations in a + NSEC3 record(150). These values are sensible, but there is, as yet, + no standardisation on the values for a "conforming" domain, so a + new option --dnssec-limit is provided should they need to be altered. + The algorithm to validate DS records has also been altered to reduce + the maximum work from cross product of the number of DS records and + number of DNSKEYs to the cross product of the number of DS records + and supported DS digest types. As the number of DS digest types + is in single figures, this reduces the exposure. + + Credit is due to Elias Heftrig, Haya Schulmann, Niklas Vogel, + and Michael Waidner from the German National Research Center for + Applied Cybersecurity ATHENE for finding this vulnerability. + + CVE 2023-50387 and CVE 2023-50868 apply. + Note that the is a security vulnerablity only when DNSSEC validation + is enabled. + + version 2.89 Fix bug introduced in 2.88 (commit fe91134b) which can result in corruption of the DNS cache internal data structures and diff -Nru dnsmasq-2.89/Makefile dnsmasq-2.90/Makefile --- dnsmasq-2.89/Makefile 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/Makefile 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -# dnsmasq is Copyright (c) 2000-2022 Simon Kelley +# dnsmasq is Copyright (c) 2000-2024 Simon Kelley # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -29,6 +29,7 @@ COPTS = RPM_OPT_FLAGS = LIBS = +LUA = lua ################################################################# @@ -60,8 +61,8 @@ idn2_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFIG) --libs libidn2` ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack` ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack` -lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.2` -lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.2` +lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags $(LUA)` +lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs $(LUA)` nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags 'nettle hogweed' \ HAVE_CRYPTOHASH $(PKG_CONFIG) --cflags nettle \ HAVE_NETTLEHASH $(PKG_CONFIG) --cflags nettle` diff -Nru dnsmasq-2.89/VERSION dnsmasq-2.90/VERSION --- dnsmasq-2.89/VERSION 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/VERSION 2024-02-13 13:49:15.000000000 +0000 @@ -1 +1 @@ - (HEAD -> master, tag: v2.89, origin/master, origin/HEAD) + (HEAD -> master, tag: v2.90) diff -Nru dnsmasq-2.89/dbus/DBus-interface dnsmasq-2.90/dbus/DBus-interface --- dnsmasq-2.89/dbus/DBus-interface 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/dbus/DBus-interface 2024-02-13 13:49:15.000000000 +0000 @@ -44,6 +44,14 @@ -------------------- Takes boolean, sets or resets the --filterwin2k option. +SetFilterA +------------------------ +Takes boolean, sets or resets the --filter-A option. + +SetFilterAAAA +------------------------ +Takes boolean, sets or resets the --filter-AAAA option. + SetBogusPrivOption ------------------ Takes boolean, sets or resets the --bogus-priv option. diff -Nru dnsmasq-2.89/debian/changelog dnsmasq-2.90/debian/changelog --- dnsmasq-2.89/debian/changelog 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/changelog 2024-08-28 16:52:09.000000000 +0000 @@ -1,3 +1,136 @@ +dnsmasq (2.90-4~deb12u1) bookworm; urgency=medium + + * Non-maintainer upload. + * Backport to bookworm, fixing: + - CVE-2023-28450 - Reduce default maximum EDNS.0 UDP packet size due to DNS + Flag Day 2020 + - CVE-2023-50387, CVE-2023-50868 - DNSSEC validation CPU exhaustion + ("Keytrap") + + -- Lee Garrett Wed, 28 Aug 2024 18:52:09 +0200 + +dnsmasq (2.90-4) unstable; urgency=medium + + [ Sven Geuer ] + * d/dnsmasq-base.postrm: + - Handle the rare case "userdel" not being available (closes: #1071142). + * Bump Standards-Version to 4.7.0. + + -- Lee Garrett Wed, 28 Aug 2024 18:52:07 +0200 + +dnsmasq (2.90-3) unstable; urgency=medium + + * Update d/control: + - Add passwd to Depends of dnsmasq-base(-lua) (closes: #1064518). + - Update the Vcs-* fields (closes: #1065347). + - Fix lintian issue build-depends-on-obsolete-package. + - Add myself as uploader. + * Update d/copyright: + - Fix lintian issue missing-field-in-dep5-copyright. + * Add missing CVE number to the 2.90-1 change log. + + -- Sven Geuer Sat, 09 Mar 2024 20:18:53 +0100 + +dnsmasq (2.90-2) unstable; urgency=medium + + [ Sven Geuer ] + * Relax limits imposed by d/t/functions.d/ip-addr.patterns to allow for + successful tests on ci.debian.net. + + -- Simon Kelley Wed, 14 Feb 2024 11:33:14 +0000 + +dnsmasq (2.90-1) unstable; urgency=medium + + [ Simon Kelley ] + * New upstream. (closes: #1033165) + CVE-2023-28450 + * Move hard-coding of Lua version from the upstream Makefile + to d/rules. + * Security fixes for Keytrap - DNSSEC validation CPU exhaustion. + CVE-2023-50387 and CVE-2023-50868 + [ Sven Geuer ] + * Introduce autokpgtests per d/tests/* (closes: #1034135). + * Switch to dpkg-source 3.0 (quilt) format (closes: #1007041). + * doc.html: Add patch to eliminate privacy breaches leaving the Donations + paragraph as untouched as possible. + * Prepend dnsmasq. to default, init, preinst, postinst, prerm, postrm. + * Rename d/systemd.service to d/dnsmasq.service. + * Rename d/systemd@.service to d/dnsmasq@.service. + * Refactor d/rules to use the DH sequencer and fix major lintian issues + (closes: #844989, #1040923, #1063551). + Modified files: + - d/rules + Complete rewrite making use of debhelper and its tools, fixes lintian + warning debian-rules-sets-dpkg-architecture-variable. + - d/control + Build-Depends, Pre-Depends, Depends added or changed as needed, lintian + error depends-on-obsolete-package fixed. + - d/dnsmasq.default + ENABLED removed and comment changed to fix lintian error + init.d-script-should-always-start-service. + - d/dnsmasq.init + Remove handling of obsolete ENABLED flag. + Extract code used with System-V-style init and systemd into + d/init-system-common, extract code used with systemd only + into d/systemd-helper. This fixes lintian warning + systemd-service-file-wraps-init-script. + Drop workaround for hypothetically non-existent file + /lib/lsb/init-functions, it has been around for more than a decade. + - d/dnsmasq.service, d/dnsmasq@.service + Adapt these files to make use of init-system-common and systemd-helper. + - d/dnsmasq.{post,pre}{inst,rm} + Rely mostly on the script snippets created by the DH tools to get + things done, implicitly fixes the lintian warnings + maintainer-script-should-not-use-dpkg-maintscript-helper and + command-with-path-in-maintainer-script. + - d/resolvconf* + Change file mode bits to 0755, the installed files need it + New files: + - d/dnsmasq.{install,links,maintscript} + - d/dnsmasq-base.{dirs,docs,install} + - d/dnsmasq-base-lua.{dirs,docs,install,links} + - d/dnsmasq-utils.{install,manpages} + The DH tools use these to install what was scripted explicitly + in the previous version of the d/rules file, + lintian warning dbus-policy-in-etc fixed + - d/init-system-common + - d/systemd-helper + These files contain slightly modified code formerly part of in + d/dnsmasq.init. + Deleted files: + - d/*conffiles + - d/lintian-override + - d/installed-marker + These are not in use anymore. + * Deal with a removed conffile and changed links. + Modified files: + - d/dnsmasq-base.{postinst,postrm} + New files: + - d/dnsmasq-base.maintscript + - d/dnsmasq-base-lua.maintscript + * Add watch file and upstream's signing key. + New files: + - d/watch + - d/u/signing-key.asc + * Remove dependency on package adduser. + Modified files: + - d/control + - d/dnsmasq.post{inst,rm} + * Refactor d/copyright to comply with DEP 5 (closes: #966505). + * Remove trailing whitespace from various files under debian/. + * Bump Standards-Version to 4.6.2. + * Specify Rules-Requires-Root. + * Update http:// to https:// with Homepage, Vcs-Git and Vcs-Browser. + * Introduce d/u/metadata. + * Fix lintian issue duplicate-short-description. + * Fix lintian issue capitalization-error-in-description. + * Bump Lua version to 5.4 (closes: #1050750). + Modified files: + - d/control + - d/t/functions.d/log.patterns + + -- Simon Kelley Fri, 23 Jan 2024 22:52:01 +0000 + dnsmasq (2.89-1) unstable; urgency=low * New upstream. @@ -95,7 +228,7 @@ dnsmasq (2.81-2) unstable; urgency=low * Fix FTBFS on kFreeBSD. (closes: #958100) - + -- Simon Kelley Sat, 18 Apr 2020 18:34:15 +0000 dnsmasq (2.81-1) unstable; urgency=low @@ -113,7 +246,7 @@ * Fix build faliure with newer Nettle libraries. (closes: #940985) * Support runscript init-system (closes: #929884) * Security fix for CVE-2019-14834 (closes: #948373) - + -- Simon Kelley Wed, 8 Apr 2020 17:33:15 +0000 dnsmasq (2.80-1) unstable; urgency=low @@ -592,7 +725,7 @@ * New upstream. * Bump standards-version to 3.8.2 (no changes needed). - * Ignore files named *.dpkg-old, *.dpkg-new and *.dpkg-dist + * Ignore files named *.dpkg-old, *.dpkg-new and *.dpkg-dist in /etc/dnsmasq.d * Provide a facility in /etc/default/dnsmasq to disable dnsmasq's interaction with the resolvconf package. This is needed because @@ -601,31 +734,31 @@ * Check for duplicate names/addresses in /etc/ethers. (closes: #523787) * Set the system locale in the environment before invoking dnsmasq, so that translated messages work, and IDN uses the correct charset. - + -- Simon Kelley Fri, 4 Oct 2009 14:01:14 +0000 dnsmasq (2.50-1) unstable; urgency=high - + * New upstream, fixes remote vulns in TFTP server. - Bugtraq id: 36120,36121 CVE: 2009-2957,2009-2958 - + Bugtraq id: 36120,36121 CVE: 2009-2957,2009-2958 + -- Simon Kelley Fri, 21 Aug 2009 10:25:13 +0000 - + dnsmasq (2.49-1) unstable; urgency=low - + * New upstream. * Log TFTP "file not found" errors. (closes: #532201) - + -- Simon Kelley Mon, 8 Jun 2009 22:03:23 +0000 - + dnsmasq (2.48-2) unstable; urgency=low - + * Change dnsmasq -> dnsmasq-base dependency to >= to allow binNMU, fixes Lintian error. * Bump standards-version to 3.8.1 - + -- Simon Kelley Fri, 5 Jun 2009 10:58:33 +0000 dnsmasq (2.48-1) unstable; urgency=low @@ -637,14 +770,14 @@ installing the latest dnsmasq will install the latest dnsmasq-base too. (closes: #523955) * Add nodhcp DEB_BUILD_OPTIONS option. - + -- Simon Kelley Fri, 29 May 2009 10:20:23 +0000 dnsmasq (2.47-3) unstable; urgency=low * Fix bashism in init script. (closes: #514397) * Tweak logging in init script. - + -- Simon Kelley Sat, 7 Feb 2009 19:25:23 +0000 dnsmasq (2.47-2) unstable; urgency=low @@ -652,57 +785,57 @@ * Check that /etc/init.d/dnsmasq is executable in postinst in case the daemon has been disabled that way. (closes: #514314) * Ensure that /var/run/dnsmasq exists and has the right permissions - before running dnsmasq. On some systems /var/run is cleared over + before running dnsmasq. On some systems /var/run is cleared over reboot. (closes: #514317) - + -- Simon Kelley Fri, 6 Feb 2009 09:38:21 +0000 - + dnsmasq (2.47-1) unstable; urgency=low * New upstream. - * Handle the "ENABLED" flag in the init script a bit more + * Handle the "ENABLED" flag in the init script a bit more intelligently. The "stop" and "status" functions continue - to work even when disabled, but a failed "stop" becomes + to work even when disabled, but a failed "stop" becomes silent and returns zero exit code. - * Don't explicitly kill dnsmasq at system shutdown, rely on the + * Don't explicitly kill dnsmasq at system shutdown, rely on the sendsigs script instead which is quicker. (closes: #506734) * Store the PID-file in /var/run/dnsmasq. This directory is owned by user "dnsmasq", so that dnsmasq can delete the PID-file on - shutdown. This ensures that the the PID-file goes even when dnsmasq - is stopped by sendsigs. (closes: #508560) + shutdown. This ensures that the the PID-file goes even when dnsmasq + is stopped by sendsigs. (closes: #508560) * Bump standards-version to 3.8.0 (no changes required.) * /usr/sbin/adduser -> adduser in postinst. Lintian fix. - * Handle IPv6 addresses in "tentative" state better. (closes: #507646) + * Handle IPv6 addresses in "tentative" state better. (closes: #507646) * Add DBus introspection support. (closes: #508774) * Fix Dbus configuration. (closes: #510649) - + -- Simon Kelley Mon, 2 Feb 2009 13:39:11 +0000 dnsmasq (2.46-1) unstable; urgency=low * New upstream. (closes: #499162) (closes: #499007) * Remove from init script start-stop-daemon call to kill - child processes. This is not needed since dnsmasq is + child processes. This is not needed since dnsmasq is carefully written to kill child processes, and it interacts badly with "private" instances of dnsmasq. (closes: #505523) * Provide /etc/dnsmasq.d and alter the installed /etc/default/dnsmasq so that /etc/dnsmasq.d is read. This provides a drop-directory where - libvirt can add options to make the system dnsmasq automatically + libvirt can add options to make the system dnsmasq automatically play nice with libvirt's private instances. (closes: #505522) - + -- Simon Kelley Thu, 13 Nov 2008 20:15:31 +0000 dnsmasq (2.45-1) unstable; urgency=high * New upstream - fixes regression when min-port not set. - + -- Simon Kelley Sun, 20 Jul 2008 19:27:11 +0000 - + dnsmasq (2.44-1) unstable; urgency=high * New upstream - bugfix release for 2.43. * Fix crash in netlink code. (closes: #491289) - + -- Simon Kelley Fri, 11 Jul 2008 19:39:10 +0000 dnsmasq (2.43-1) unstable; urgency=high @@ -710,25 +843,25 @@ * New upstream. * Implement source-port randomisation and better random number generator as defence against CVE-2008-1447 (closes: #490123) - + -- Simon Kelley Tue, 17 Jun 2008 11:55:38 +0000 dnsmasq (2.42-4) unstable; urgency=low * Fix botch in postinst introduced in 2.42-2. (closes: #486616) - + -- Simon Kelley Tue, 17 Jun 2008 11:39:10 +0000 dnsmasq (2.42-3) unstable; urgency=low * Fix thinko in init script, breaks status command. (closes: #486455) - + -- Simon Kelley Mon, 16 Jun 2008 11:26:20 +0000 dnsmasq (2.42-2) unstable; urgency=low * Error check in postinst file (closes: #485645) - + -- Simon Kelley Tue, 10 Jun 2008 20:25:10 +0000 dnsmasq (2.42-1) unstable; urgency=low @@ -738,13 +871,13 @@ * Use LSB log_*_msg rather than echo in init script. (closes: #473117) * Fix agent-id echo problem. (closes: #473015) * Fixup changing /usr/share/doc/dnsmasq to symlink. (closes: #468763) - + -- Simon Kelley Wed, 27 Feb 2008 21:15:28 +0000 dnsmasq (2.41-2) unstable; urgency=low * Fix rules to build binary-arch and binary-indep correctly. - + -- Simon Kelley Wed, 27 Feb 2008 19:57:10 +0000 dnsmasq (2.41-1) unstable; urgency=low @@ -754,11 +887,11 @@ * Fix DHCP problem interoperating with Sony Ericsson K610i (closes: #451871) * Split binary packages into dnsmasq and dnsmasq-base (closes: #463407) * Add warnings about bad effects of --filterwin2k to default config - file. (closes: #464357) + file. (closes: #464357) * Don't declare Provides: $named in LSB header. (closes: #464512) * Remove conflict with pdnsd. (closes: #464691) * Add ability to disable dnsmasq in /etc/default/dnsmasq. (closes: #465062) - + -- Simon Kelley Thu, 31 Jan 2008 20:25:28 +0000 dnsmasq (2.40-1) unstable; urgency=low @@ -767,11 +900,11 @@ * Fix manpage typo. (closes: #429412) * Fix dnsmasq.conf typos (closes: #429929) * Handle DEB_BUILD_OPTIONS nostrip and noopt (closes: #436784) - * Add DEB_BUILD_OPTIONS for nodocs, notftp, noipv6, + * Add DEB_BUILD_OPTIONS for nodocs, notftp, noipv6, nodbus, noi18n and nortc. * Create DEBIAN/md5sums file in package. * Add status function to init script. (closes: #439316) - + -- Simon Kelley Thu, 9 Aug 2007 10:24:18 +0000 dnsmasq (2.39-1) unstable; urgency=low @@ -779,19 +912,19 @@ * New upstream. * Provide example config file in /usr/share/doc/dnsmasq/examples as well as /etc/dnsmasq.conf, so it's available for reference. - + -- Simon Kelley Thu, 13 Feb 2007 10:02:38 +0000 dnsmasq (2.38-1) unstable; urgency=low * New upstream (closes: #410185) - + -- Simon Kelley Tue, 6 Feb 2007 21:14:58 +0000 dnsmasq (2.37-1) unstable; urgency=low * New upstream. - + -- Simon Kelley Thu, 25 Jan 2007 10:44:18 +0000 dnsmasq (2.36-1) unstable; urgency=low @@ -799,14 +932,14 @@ * New upstream. (closes: #400037) * Don't fail to purge if deluser command is not available. * Add one second sleep to resolvconf script. (closes: #398961) - * Fix dnsmasq.conf typo (closes: #405314) - + * Fix dnsmasq.conf typo (closes: #405314) + -- Simon Kelley Tue, 31 Oct 2006 10:24:58 +0000 dnsmasq (2.35-1) unstable; urgency=low * New upstream. - + -- Simon Kelley Wed, 18 Oct 2006 09:23:28 +0000 dnsmasq (2.34-1) unstable; urgency=low @@ -814,9 +947,9 @@ * New upstream. * Includes --clear-on-reload flag. (loses: #391654) * Don't any longer set the "domain-needed" and "bogus-priv" flags in the - * the default-installed dnsmasq.conf. These can generate puzzling + * the default-installed dnsmasq.conf. These can generate puzzling * behaviour for people who get them without asking. - + -- Simon Kelley Wed, 9 Aug 2006 09:23:28 +0000 dnsmasq (2.33-1) unstable; urgency=low @@ -825,71 +958,71 @@ * Remove bashism from Makefile (closes: #375409) * Added Provides: $named to LSB header in init script. * Add --dns-forward-max flag. (closes: #377506) - + -- Simon Kelley Sun, 25 June 2006 18:03:13 +0000 dnsmasq (2.32-2) unstable; urgency=low * Added LSB tags to init.d startup script. (closes: #374650) - + -- Simon Kelley Sun, 25 June 2006 17:55:11 +0000 dnsmasq (2.32-1) unstable; urgency=low * New upstream. - + -- Simon Kelley Mon, 8 May 2006 09:23:28 +0000 dnsmasq (2.31-1) unstable; urgency=high * New upstream. (closes: #364800) * Compile in Dbus support now that suitable Dbus packages exist. - * Don't stop an old dnsmasq process, until a new one is ready, + * Don't stop an old dnsmasq process, until a new one is ready, when upgrading. (closes: #366224) * Move to standards-version 3.7.2 (no changes needed). - + -- Simon Kelley Sat, 6 May 2006 11:58:22 +0000 dnsmasq (2.30-1) unstable; urgency=low * New upstream, fixes crash with DHCP broadcast replies. - + -- Simon Kelley Sun, 23 Apr 2006 14:58:22 +0000 dnsmasq (2.29-1) unstable; urgency=low * New upstream. (closes: #363244) (closes: #363340) - * Made config options clearer in src/config.h and + * Made config options clearer in src/config.h and clarify ISC integration status in Debian readme. (closes: #364250) - + -- Simon Kelley Tue, 18 Apr 2006 10:26:12 +0000 dnsmasq (2.28-1) unstable; urgency=low * New upstream. (closes: #359956) (closes: #362499) * Added firestarter info to FAQ. (closes: #359139) - + -- Simon Kelley Tue, 14 Mar 2006 19:20:12 +0000 dnsmasq (2.27-1) unstable; urgency=low * New upstream. * Workaround buggy Microsoft DHCP clients. (closes: #355008) - + -- Simon Kelley Wed, 1 Feb 2006 17:05:12 +0000 dnsmasq (2.26-1) unstable; urgency=high * New upstream. (Fixes possible crash in 2.25, hence urgency). - + -- Simon Kelley Sun, 22 Jan 2006 11:05:22 +0000 dnsmasq (2.25-1) unstable; urgency=low * Remove bashisms in postinst and prerm scripts. - * Remove misconceived dependency on locales. + * Remove misconceived dependency on locales. * Depend on adduser. - + -- Simon Kelley Thu, 01 Dec 2005 21:02:12 +0000 dnsmasq (2.24-1) unstable; urgency=low @@ -899,7 +1032,7 @@ * Add build support for I18N and gettext. * Fixed manpage typos. (closes: #336413) * Create a dnsmasq-unique userid for the daemon to run as. (closes: #338353) - + -- Simon Kelley Sat, 03 Sep 2005 20:02:32 +0000 dnsmasq (2.23-1) unstable; urgency=low @@ -908,32 +1041,32 @@ * Fix manpage typos. (closes: #304984) * Add support for DNSMASQ_EXCEPT in /etc/defaults/dnsmasq. putting "lo" in this also disables resolvconf support. - * No longer delete pre-existing /etc/init.d symlinks. The - change in default runlevels which necessitated this - is now ancient history and anyway the startup script now + * No longer delete pre-existing /etc/init.d symlinks. The + change in default runlevels which necessitated this + is now ancient history and anyway the startup script now behaves when called twice. (closes: #312111) * Tightened config-file parser. (closes: #317030) - + -- Simon Kelley Tue, 02 Aug 2005 13:17:22 +0000 dnsmasq (2.22-2) unstable; urgency=low - * Make the resolv.conf polling code resistant to + * Make the resolv.conf polling code resistant to backwards-moving system clocks. (closes: #306117) (closes: #300694) - + -- Simon Kelley Wed, 04 May 2005 13:25:23 +0000 dnsmasq (2.22-1) unstable; urgency=low * New upstream. * Fixed broken-ness when read /etc/ethers. (closes: #301999) - + -- Simon Kelley Thur, 24 Mar 2005 17:10:13 +0000 dnsmasq (2.21-1) unstable; urgency=low * New upstream. - + -- Simon Kelley Sat, 29 Jan 2005 16:05:13 +0000 dnsmasq (2.20-1) unstable; urgency=low @@ -941,7 +1074,7 @@ * New upstream. * Fix shadowed CNAME-target problem. (closes: #286654) * Add --localise-queries option. (closes: #291367) - + -- Simon Kelley Fri, 17 Dec 2004 17:35:23 +0000 dnsmasq (2.19-1) unstable; urgency=high @@ -949,37 +1082,37 @@ * New upstream. * Fix another IPv6 interface enumeration problem. (closes: #285182) * Uploading at high priority since 285182 is really RC. - + -- Simon Kelley Sat, 11 Dec 2004 20:39:33 +0000 dnsmasq (2.18-2) unstable; urgency=low * Revert startup to not start from rcS. Starting in rcS * causes problems if interfaces are not available at that - * point. Users who need this facility should manually + * point. Users who need this facility should manually * make rcS.d symlinks. (closes: #283239) -- Simon Kelley Sat, 27 Nov 2004 16:33:12 +0000 - + dnsmasq (2.18-1) unstable; urgency=low * New upstream. * Reset cache statistics when clearing the cache. (closes: #281817) * Fix problems with bind-interfaces and IPv6. (closes: #282192) * Fix problems upgrading when restarting dnsmasq fails. - + -- Simon Kelley Tue, 16 Nov 2004 17:33:32 +0000 dnsmasq (2.17-1) unstable; urgency=high - * New upstream - fixes crash, hence high urgency. - * Clarified log message when a record in /etc/hosts + * New upstream - fixes crash, hence high urgency. + * Clarified log message when a record in /etc/hosts and a DHCP name clash. (closes: #275420) * Start dnsmasq just before portmap and nfs mounts from rcS.d DNS is required at this stage to use the net. (closes: #280434) * Make "bind-interfaces" apply to IPv6 interfaces. (closes: #278492) * Allow a list if interfaces as arg to the --interface and - --except-interface options. (closes: #279063) + --except-interface options. (closes: #279063) -- Simon Kelley Tue, 26 Oct 2004 20:39:33 +0000 @@ -987,7 +1120,7 @@ * Rename variable in cache.c which clashes with C headers under gcc-3.4 (closes: #277893) - + -- Simon Kelley Mon, 25 Oct 2004 16:03:24 +0000 dnsmasq (2.16-1) unstable; urgency=high @@ -998,26 +1131,26 @@ packets with bad checksum. * Fix bad interaction with polipo. (closes: #275754) * Cache CNAMEs better. (closes: #276289) - + -- Simon Kelley Mon, 04 Oct 2004 15:25:44 +0000 dnsmasq (2.15-1) unstable; urgency=low * New upstream. * Fix NXDOMAIN/NODATA confusion for locally known names. (closes: #271564) - + -- Simon Kelley Wed, 15 Sep 2004 15:01:44 +0000 dnsmasq (2.14-1) unstable; urgency=low * New upstream. - + -- Simon Kelley Sat, 28 Aug 2004 20:39:33 +0000 dnsmasq (2.13-1) unstable; urgency=high * New upstream - fixes crash. (closes #265313) - + -- Simon Kelley Thur, 12 Aug 2004 12:45:23 +0000 dnsmasq (2.12-1) unstable; urgency=low @@ -1026,13 +1159,13 @@ * Log types of incoming queries (closes: #230123). * Don't set "filterwin2k" by default in the included config file - it breaks SRV lookups and Kerberos. - + -- Simon Kelley Sun, 8 Aug 2004 19:58:13 +0000 dnsmasq (2.11-1) unstable; urgency=low * New upstream. - + -- Simon Kelley Wed, 28 July 2004 21:59:33 +0000 dnsmasq (2.10-1) unstable; urgency=low @@ -1048,7 +1181,7 @@ dnsmasq (2.9-2) unstable; urgency=low * Fix typo in debian/control (closes: #255762) - + -- Simon Kelley Wed, 23 Jun 2004 20:40:13 +0000 dnsmasq (2.9-1) unstable; urgency=low @@ -1060,7 +1193,7 @@ * Updated README.debian (closes: #253429) * Changed startup message to mention DHCP as well as DNS. * New resolvconf update script (closes: #254765) - + -- Simon Kelley Wed, 26 May 2004 12:35:23 +0000 dnsmasq (2.8-1) unstable; urgency=low @@ -1070,10 +1203,10 @@ DHCP leases over a restart. (closes: #248829) -- Simon Kelley Thur, 13 May 2004 18:40:12 +0000 - + dnsmasq (2.7-2) unstable; urgency=low - * New version of resolvconf script from Thomas Hood with the + * New version of resolvconf script from Thomas Hood with the following changes: (closes: #247695) * Doesn't include nameservers listed in the lo.inet or lo.inet6 interface records created by "ifup lo" @@ -1081,7 +1214,7 @@ * Eliminates duplicate nameserver addresses * Updates /var/run/dnsmasq/resolv.conf atomically * Doesn't generate empty lines - + -- Simon Kelley Tue, 11 May 2004 22:35:12 +0000 dnsmasq (2.7-1) unstable; urgency=low @@ -1096,7 +1229,7 @@ to be equivalent to restart. This is needed to be policy compliant since SIGHUP doesn't cause dnsmasq to reload its configuration file, only the /etc/hosts, /etc/resolv.conf etc. (closes: #244208) - + -- Simon Kelley Sun, 18 Apr 2004 14:40:51 +0000 dnsmasq (2.6-2) unstable; urgency=low @@ -1113,46 +1246,46 @@ readme.Debian (closes: #241700) -- Simon Kelley Tue, 13 Apr 2004 18:37:55 +0000 - + dnsmasq (2.6-1) unstable; urgency=low * New upstream. * New version adds back ability to read ISC dhcpd lease files for backwards compatibility. (closes: #229684) (closes: #236421) * Fix parsing of # characters in options file. (closes: #241199) - + -- Simon Kelley Sun, 21 Mar 2004 19:59:25 +0000 - + dnsmasq (2.5-1) unstable; urgency=low - * New upstream, includes fix for IP-alias related + * New upstream, includes fix for IP-alias related problem. (closes: #238268) - + -- Simon Kelley Sun, 14 Mar 2004 08:32:43 +0000 dnsmasq (2.4-3) unstable; urgency=low * Fixed "bind-interfaces" option, even when an "interface" option is given also. - + -- Simon Kelley Fri, 12 Mar 2004 08:14:23 +0000 dnsmasq (2.4-2) unstable; urgency=low * Fixed "bind-interfaces" option (closes: #237543). - + -- Simon Kelley Fri, 12 Mar 2004 07:30:25 +0000 dnsmasq (2.4-1) unstable; urgency=low * New upstream. - + -- Simon Kelley Thurs, 11 Mar 2004 07:59:55 +0000 dnsmasq (2.3-1) unstable; urgency=low * New upstream. - + -- Simon Kelley Tues, 03 Feb 2004 20:33:10 +0000 dnsmasq (2.2-1) unstable; urgency=low @@ -1165,13 +1298,13 @@ dnsmasq (2.1-1) unstable; urgency=low * New upstream. - * Allow addresses in /etc/hosts to be used for + * Allow addresses in /etc/hosts to be used for DHCP leases (closes: #229681) * Fix lease time processing. (closes: #229682) (closes: #229687) * Fix example conf file. (closes: #229683) (closes: #229701) * Allow address 0.0.0.0 to mean "self" in dhcp-option. (closes: #229685) - * Cope with ENODEV return from bind of - IPv6 server socket (closes: #229607) + * Cope with ENODEV return from bind of + IPv6 server socket (closes: #229607) * Document the strict-order option in dnsmasq.conf (closes: #229272) * Fix local-only domain setting. (closes: #229846) * Updates Debian readme to mention resolvconf and point at the @@ -1181,14 +1314,14 @@ dnsmasq (2.0-1) unstable; urgency=low - * New upstream: This removes the ability to read the - the leases file of ISC DHCP and replaces it with a built-in - DHCP server. Apologies in advance for breaking backwards + * New upstream: This removes the ability to read the + the leases file of ISC DHCP and replaces it with a built-in + DHCP server. Apologies in advance for breaking backwards compatibility, but this replaces a bit of a hack (the ISC stuff) with a nicely engineered and much more appropriate solution. Wearing my upstream-maintainer hat, I want to lose the hack now, rather than have to support it into Sarge. - * New upstream closes some bugs since they become + * New upstream closes some bugs since they become irrelevant. (closes: #197295) * Ensure that /var/run and /var/lib/misc exist. * Remove sed dependency, which was a mistake. @@ -1197,21 +1330,21 @@ -- Simon Kelley Sun, 16 Jan 2004 19:35:49 +0000 dnsmasq (1.18-2) unstable; urgency=low - + * Fixed manpage typo (closes: #220961) * Added dependency for sed. (closes: #222401) - * Check for complete resolvconf installation before + * Check for complete resolvconf installation before calling it. (closes: #223442) * Added Links section to doc.html - + -- Simon Kelley Sat, 27 Dec 2003 20:21:15 +0000 - + dnsmasq (1.18-1) unstable; urgency=low * New upstream which does round-robin. (closes: #215460) * Removed conflicts with other dns servers since it is now - possible to control exactly where dnsmasq listens on multi-homed - hosts, making co-existence with another nameserver + possible to control exactly where dnsmasq listens on multi-homed + hosts, making co-existence with another nameserver a viable proposition. (closes #176163) * New upstream allows _ in hostnames and check for illegal names in /etc/hosts. (closes: #218842) @@ -1225,39 +1358,39 @@ -- Simon Kelley Wed, 8 Oct 2003 14:38:29 +0000 dnsmasq (1.16-1) unstable; urgency=low - + * New upstream. * Renamed Debian README to the standard README.Debian. (closes: #211577) * Updated the installed /etc/dnsmasq.conf to reflect new options. - + -- Simon Kelley Tues, 16 Sep 2003 23:18:59 +0000 dnsmasq (1.15-1) unstable; urgency=low - + * New upstream. - + -- Simon Kelley Tues, 16 Sep 2003 21:48:49 +0000 dnsmasq (1.14-1) unstable; urgency=low - + * New upstream. * Use invoke-rc.d in postinst and prerm scripts when available. * Stop dnsmasq later (at priority 85). (closes: #200625) * Updated /etc/resolvconf/update.d/dnsmasq. (closes: #202609) * Suggest resolvconf. (closes: #208093) - + -- Simon Kelley Tues, 2 Sep 2003 16:43:29 +0000 dnsmasq (1.13-4) unstable; urgency=high - - * Ignore failures in stopping existing dnsmasq + + * Ignore failures in stopping existing dnsmasq processes. (closes: #204127) (closes: #204129) * Added download source to copyright. (closes: #206647) - + -- Simon Kelley Tues, 2 Sep 2003 15:28:28 +0000 dnsmasq (1.13-3) unstable; urgency=low - + * Moved /etc/resolvconf/update.d/dnsmasq script into this package. * Don't call resolvconf from /etc/init.d/dnsmasq if dnsmasq fails to start. (Patch from Thomas Hood.) @@ -1265,7 +1398,7 @@ -- Simon Kelley Mon, 7 Jul 2003 20:55:29 +0000 dnsmasq (1.13-2) unstable; urgency=low - + * Added support for the resolvconf nameserver configuration package. -- Simon Kelley Sun, 22 Jun 2003 20:30:19 +0000 @@ -1286,19 +1419,19 @@ * Added examples of "local" and "address" options to dnsmasq.conf. * Remove /usr/doc symlink code. * Remove period from end of description field. - + -- Simon Kelley Sat, 8 Mar 2003 12:16:09 +0000 dnsmasq (1.11-2) unstable; urgency=low * Fixed thinko in example dnsmasq.conf. (closes: #180410) - + -- Simon Kelley Mon, 24 Feb 2003 20:06:19 +0000 dnsmasq (1.11-1) unstable; urgency=low * New upstream. - + -- Simon Kelley Tues, 12 Jan 2003 22:25:17 -0100 dnsmasq (1.10-1) unstable; urgency=low @@ -1306,134 +1439,134 @@ * New upstream. * Force service to stop in postinst before restarting. I don't understand the circumstances under which it would still be running at - this point, but this is the correct fix anyway. (closes: #169718) - * Add /etc/dnsmasq.conf as a conffile and add a comment to - /etc/default/dnsmasq deprecating its use and recommending + this point, but this is the correct fix anyway. (closes: #169718) + * Add /etc/dnsmasq.conf as a conffile and add a comment to + /etc/default/dnsmasq deprecating its use and recommending /etc/dnsmasq.conf instead, since upstream now supports this. - + -- Simon Kelley Mon, 9 Oct 2002 19:05:34 -0100 dnsmasq (1.9-1) unstable; urgency=low * New upstream. - + -- Simon Kelley Mon, 23 Sept 2002 21:35:07 -0100 dnsmasq (1.8-1) unstable; urgency=low - + * New upstream. - + -- Simon Kelley Mon, 12 Aug 2002 21:56:17 -0100 dnsmasq (1.7-1) unstable; urgency=low - + * New upstream including better group-id manipulation. (closes: #152212) * Conflict with bind9 (closes: #151812) * Added more options to startup script. (closes: #148535) - + -- Simon Kelley Sun, 14 July 2002 20:23:14 -0100 dnsmasq (1.6-1) unstable; urgency=low - + * New upstream. * Fixed documentation typos. (closes: #144637) * Fixed failure to remove package if daemon not running. (closes: #147083) * Changed upload to tarball-and-diff. (closes: #144638) - + -- Simon Kelley Sun, 19 May 2002 22:30:17 -0100 dnsmasq (1.5-1) unstable; urgency=medium - + * New upstream (includes hotmail.com fix). * Fixed DHCP lease file bug. (closes: #143778) * Fixed failure of "reload" command in startup script (closes: #141021) * Allow more than one interface name in the DNSMASQ_INTERFACE variable. - + -- Simon Kelley Sun, 14 Apr 2002 16:39:13 -0100 - + dnsmasq (1.4-2) unstable; urgency=low - + * Fixed snafu in startup script (closes: #139760) - + -- Simon Kelley Sun, 24 Mar 2002 23:06:18 +0000 - + dnsmasq (1.4-1) unstable; urgency=low - + * New upstream - + -- Simon Kelley Thurs, 7 Mar 2002 21:02:05 +0000 - + dnsmasq (1.3-1) unstable; urgency=low - + * New upstream -- Simon Kelley Fri, 15 Feb 2002 20:45:01 +0000 - + dnsmasq (1.2-4) unstable; urgency=low - + * Updated standards-version. * More aggressive strip of binaries. * Added depends: netbase. * distribution->unstable for upload. * Updated readme.Debian since config in /etc/default/dnsmasq now. * Updated readme.Debian to reflect fact that this package is official now! - + -- Simon Kelley Fri, 15 Feb 2002 20:45:01 +0000 - + dnsmasq (1.2-3) stable; urgency=low - + * Added Suggests: and Conflicts: fields to control file. - + -- Simon Kelley Thurs, 14 Feb 2002 20:33:47 +0000 - + dnsmasq (1.2-2) stable; urgency=low - + * Many packaging fixes, to please lintian * Added extended description. * Fixed copyright file. * Compressed everything in /usr/share/doc/dnsmasq. * Added code to remove /usr/doc/dnsmasq to prerm script. * Moved configuration from /etc/init.d/dnsmasq to /etc/default/dnsmasq - + -- Simon Kelley Sat, 02 Feb 2002 18:54:37 +0000 - + dnsmasq (1.2-1) stable; urgency=low - + * New upstream * Added more options to startup script - + -- Simon Kelley Sat, 20 Dec 2001 21:15:07 +0000 dnsmasq (1.1-2) stable; urgency=low - + * New upstream * Strip binary * Moved manpage from section 1 to section 8 - + -- Simon Kelley Sat, 21 Oct 2001 17:32:04 -0100 dnsmasq (1.0-1) unstable; urgency=low - + * New upstream - + -- Simon Kelley Sat, 10 Oct 2001 15:52:06 -0100 - + dnsmasq (0.996-1) unstable; urgency=low - + * New upstream - + -- Simon Kelley Fri, 26 Oct 2001 10:32:06 -0100 dnsmasq (0.995-1) unstable; urgency=low - + * New upstream - + -- Simon Kelley Tue, 09 Oct 2001 16:39:07 -0100 dnsmasq (0.994-1) unstable; urgency=low - + * New upstream - + -- Simon Kelley Sat, 07 Oct 2001 15:45:04 -0100 dnsmasq (0.992-1) unstable; urgency=low @@ -1459,13 +1592,3 @@ * Initial Release. -- Simon Kelley Sat, 29 Aug 1998 20:27:27 -0400 - - - - - - - - - - diff -Nru dnsmasq-2.89/debian/conffiles dnsmasq-2.90/debian/conffiles --- dnsmasq-2.89/debian/conffiles 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/conffiles 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -/etc/init.d/dnsmasq -/etc/default/dnsmasq -/etc/dnsmasq.conf -/etc/resolvconf/update.d/dnsmasq -/etc/insserv.conf.d/dnsmasq diff -Nru dnsmasq-2.89/debian/control dnsmasq-2.90/debian/control --- dnsmasq-2.89/debian/control 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/control 2024-05-17 15:33:51.000000000 +0000 @@ -1,64 +1,67 @@ Source: dnsmasq Section: net Priority: optional -Build-depends: gettext, libnetfilter-conntrack-dev [linux-any], - libidn2-dev, libdbus-1-dev (>=0.61), libgmp-dev, +Build-Depends: dh-exec, gettext, libnetfilter-conntrack-dev [linux-any], + libidn2-dev, libdbus-1-dev (>=0.61), libgmp-dev, nettle-dev (>=2.4-3), libbsd-dev [kfreebsd-any], - liblua5.2-dev, dh-runit, debhelper-compat (= 10), - pkg-config, libnftables-dev + liblua5.4-dev, dh-runit, debhelper-compat (= 13), + pkgconf, libnftables-dev Maintainer: Simon Kelley -Homepage: http://www.thekelleys.org.uk/dnsmasq/doc.html -Vcs-Git: http://thekelleys.org.uk/git/dnsmasq.git -Vcs-Browser: http://thekelleys.org.uk/gitweb/?p=dnsmasq.git -Standards-Version: 3.9.8 +Uploaders: Sven Geuer +Homepage: https://www.thekelleys.org.uk/dnsmasq/doc.html +Vcs-Git: https://thekelleys.org.uk/git/dnsmasq-debian.git +Vcs-Browser: https://thekelleys.org.uk/gitweb/?p=dnsmasq-debian.git +Standards-Version: 4.7.0 +Rules-Requires-Root: no Package: dnsmasq Architecture: all +Pre-Depends: ${misc:Pre-Depends} Depends: netbase, dnsmasq-base, - init-system-helpers (>= 1.18~), lsb-base (>= 3.0-6), ${misc:Depends} + ${misc:Depends} Suggests: resolvconf Breaks: ${runit:Breaks} Conflicts: resolvconf (<<1.15), ${runit:Conflicts} -Description: Small caching DNS proxy and DHCP/TFTP server +Description: Small caching DNS proxy and DHCP/TFTP server - system daemon Dnsmasq is a lightweight, easy to configure, DNS forwarder and DHCP - server. It is designed to provide DNS and optionally, DHCP, to a - small network. It can serve the names of local machines which are - not in the global DNS. The DHCP server integrates with the DNS + server. It is designed to provide DNS and optionally, DHCP, to a + small network. It can serve the names of local machines which are + not in the global DNS. The DHCP server integrates with the DNS server and allows machines with DHCP-allocated addresses to appear in the DNS with names configured either in each host or - in a central configuration file. Dnsmasq supports static and dynamic + in a central configuration file. Dnsmasq supports static and dynamic DHCP leases and BOOTP/TFTP for network booting of diskless machines. Package: dnsmasq-base Architecture: any -Depends: adduser, ${shlibs:Depends} +Depends: passwd, ${misc:Depends}, ${shlibs:Depends} Breaks: dnsmasq (<< 2.63-1~) Replaces: dnsmasq (<< 2.63-1~), dnsmasq-base Recommends: dns-root-data Provides: dnsmasq-base Conflicts: dnsmasq-base-lua -Description: Small caching DNS proxy and DHCP/TFTP server +Description: Small caching DNS proxy and DHCP/TFTP server - executable This package contains the dnsmasq executable and documentation, but not the infrastructure required to run it as a system daemon. For that, install the dnsmasq package. Package: dnsmasq-base-lua Architecture: any -Depends: adduser, ${shlibs:Depends} +Depends: passwd, ${misc:Depends}, ${shlibs:Depends} Breaks: dnsmasq (<< 2.63-1~) Replaces: dnsmasq (<< 2.63-1~), dnsmasq-base Recommends: dns-root-data Provides: dnsmasq-base Conflicts: dnsmasq-base -Description: Small caching DNS proxy and DHCP/TFTP server +Description: Small caching DNS proxy and DHCP/TFTP server - executable, Lua-enabled This package contains the dnsmasq executable and documentation, but not the infrastructure required to run it as a system daemon. For that, install the dnsmasq package. This package is an alternative - to dnsmasq-base which includes the LUA interpreter. - + to dnsmasq-base which includes the Lua interpreter. + Package: dnsmasq-utils Architecture: linux-any -Depends: ${shlibs:Depends} +Depends: ${misc:Depends}, ${shlibs:Depends} Conflicts: dnsmasq (<<2.40) Description: Utilities for manipulating DHCP leases Small utilities to query a DHCP server's lease database and diff -Nru dnsmasq-2.89/debian/copyright dnsmasq-2.90/debian/copyright --- dnsmasq-2.89/debian/copyright 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/copyright 2024-05-17 15:33:51.000000000 +0000 @@ -1,21 +1,59 @@ -dnsmasq is Copyright (c) 2000-2021 Simon Kelley +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dnsmasq +Upstream-Contact: Simon Kelley +Source: https://thekelleys.org.uk/dnsmasq/ -It was downloaded from: http://www.thekelleys.org.uk/dnsmasq/ +Files: * +Copyright: 2000-2024 Simon Kelley +License: GPL-2 or GPL-3 - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 dated June, 1991, or - (at your option) version 3 dated 29 June, 2007. - - 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. +Files: src/dnssec.c +Copyright: 2012-2024 Simon Kelley + 2012 Giovanni Bajo +License: GPL-2 or GPL-3 -On Debian GNU/Linux systems, the text of the GNU general public license is -available in the file /usr/share/common-licenses/GPL-2 or -/usr/share/common-licenses/GPL-3 +Files: debian/* +Copyright: 2004-2024 Simon Kelley + 2012 Lars Bahner + 2024 Sven Geuer +License: GPL-2 or GPL-3 -The Debian package of dnsmasq was created by Simon Kelley with assistance -from Lars Bahner. +License: GPL-2 + This program is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; + version 2 dated June, 1991. + . + 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, see + . + . + On Debian systems, the full text of the GNU General Public + License can be found in the file + `/usr/share/common-licenses/GPL-2'. +License: GPL-3 + This program is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; + version 3 dated 29 June, 2007. + . + 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, see + . + . + On Debian systems, the full text of the GNU General Public + License can be found in the file + `/usr/share/common-licenses/GPL-3'. diff -Nru dnsmasq-2.89/debian/default dnsmasq-2.90/debian/default --- dnsmasq-2.89/debian/default 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/default 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -# This file has six functions: -# 1) to completely disable starting this dnsmasq instance -# 2) to set DOMAIN_SUFFIX by running `dnsdomainname` -# 3) to select an alternative config file -# by setting DNSMASQ_OPTS to --conf-file= -# 4) to tell dnsmasq to read the files in /etc/dnsmasq.d for -# more configuration variables. -# 5) to stop the resolvconf package from controlling dnsmasq's -# idea of which upstream nameservers to use. -# 6) to avoid using this dnsmasq instance as the system's default resolver -# by setting DNSMASQ_EXCEPT="lo" -# For upgraders from very old versions, all the shell variables set -# here in previous versions are still honored by the init script -# so if you just keep your old version of this file nothing will break. - -#DOMAIN_SUFFIX=`dnsdomainname` -#DNSMASQ_OPTS="--conf-file=/etc/dnsmasq.alt" - -# Whether or not to run the dnsmasq daemon; set to 0 to disable. -# Note that this is only valid when using SYSV init. For systemd, -# use "systemctl disable dnsmasq" -ENABLED=1 - -# By default search this drop directory for configuration options. -# Libvirt leaves a file here to make the system dnsmasq play nice. -# Comment out this line if you don't want this. The dpkg-* are file -# endings which cause dnsmasq to skip that file. This avoids pulling -# in backups made by dpkg. -CONFIG_DIR=/etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new - -# If the resolvconf package is installed, dnsmasq will use its output -# rather than the contents of /etc/resolv.conf to find upstream -# nameservers. Uncommenting this line inhibits this behaviour. -# Note that including a "resolv-file=" line in -# /etc/dnsmasq.conf is not enough to override resolvconf if it is -# installed: the line below must be uncommented. -#IGNORE_RESOLVCONF=yes - -# If the resolvconf package is installed, dnsmasq will tell resolvconf -# to use dnsmasq under 127.0.0.1 as the system's default resolver. -# Uncommenting this line inhibits this behaviour. -#DNSMASQ_EXCEPT="lo" diff -Nru dnsmasq-2.89/debian/dnsmasq-base-lua.dirs dnsmasq-2.90/debian/dnsmasq-base-lua.dirs --- dnsmasq-2.89/debian/dnsmasq-base-lua.dirs 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base-lua.dirs 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1 @@ +/var/lib/misc diff -Nru dnsmasq-2.89/debian/dnsmasq-base-lua.docs dnsmasq-2.90/debian/dnsmasq-base-lua.docs --- dnsmasq-2.89/debian/dnsmasq-base-lua.docs 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base-lua.docs 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,8 @@ +doc.html +setup.html +dnsmasq.conf.example +FAQ +CHANGELOG.archive +dbus/DBus-interface +debian/systemd_howto +debian/readme diff -Nru dnsmasq-2.89/debian/dnsmasq-base-lua.install dnsmasq-2.90/debian/dnsmasq-base-lua.install --- dnsmasq-2.89/debian/dnsmasq-base-lua.install 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base-lua.install 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,3 @@ +#!/usr/bin/dh-exec +debian/dbus.conf => /usr/share/dbus-1/system.d/dnsmasq.conf +trust-anchors.conf /usr/share/dnsmasq-base-lua diff -Nru dnsmasq-2.89/debian/dnsmasq-base-lua.links dnsmasq-2.90/debian/dnsmasq-base-lua.links --- dnsmasq-2.89/debian/dnsmasq-base-lua.links 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base-lua.links 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,2 @@ +usr/share/dnsmasq-base-lua usr/share/dnsmasq-base +usr/share/doc/dnsmasq-base-lua usr/share/doc/dnsmasq-base diff -Nru dnsmasq-2.89/debian/dnsmasq-base-lua.maintscript dnsmasq-2.90/debian/dnsmasq-base-lua.maintscript --- dnsmasq-2.89/debian/dnsmasq-base-lua.maintscript 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base-lua.maintscript 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,9 @@ +# With the use of debhelper /usr/share/doc/dnsmasq-base-lua has become a +# directory as required in +# https://www.debian.org/doc/debian-policy/ch-docs.html#additional-documentation +# thus /usr/share/doc/dnsmasq-base will be a link from now onwards. +symlink_to_dir /usr/share/doc/dnsmasq-base-lua /usr/share/doc/dnsmasq-base 2.89-1.1~ dnsmasq-base-lua +dir_to_symlink /usr/share/doc/dnsmasq-base /usr/share/doc/dnsmasq-base-lua 2.89-1.1~ dnsmasq-base-lua +# Due to lintian warning dbus-policy-in-etc this file has been moved to +# /usr/share/dbus-1/system.d/dnsmasq.conf and thus is not a conffile any more. +rm_conffile /etc/dbus-1/system.d/dnsmasq.conf 2.89-1.1~ dnsmasq-base-lua diff -Nru dnsmasq-2.89/debian/dnsmasq-base-lua.postinst dnsmasq-2.90/debian/dnsmasq-base-lua.postinst --- dnsmasq-2.89/debian/dnsmasq-base-lua.postinst 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base-lua.postinst 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,30 @@ +#!/bin/sh +set -e + +# Create the dnsmasq user in dnsmasq-base, so that Dbus doesn't complain. + +if [ "$1" = "configure" ]; then + # Create the user to run as. + if [ -z "`id -u dnsmasq 2> /dev/null`" ]; then + useradd --system \ + --gid nogroup \ + --comment dnsmasq \ + --home-dir /var/lib/misc --no-create-home \ + --shell /usr/sbin/nologin \ + dnsmasq + fi + + # Make the directory where we keep the pid file - this + # has to be owned by "dnsmasq" so that the file can be unlinked. + # This is only actually used by the dnsmasq binary package, not + # dnsmasq-base, but it's much easier to create it here so that + # we don't have synchronisation issues with the creation of the + # dnsmasq user. + if [ ! -d /run/dnsmasq ]; then + mkdir /run/dnsmasq + chown dnsmasq:nogroup /run/dnsmasq + fi +fi + +#DEBHELPER# + diff -Nru dnsmasq-2.89/debian/dnsmasq-base-lua.postrm dnsmasq-2.90/debian/dnsmasq-base-lua.postrm --- dnsmasq-2.89/debian/dnsmasq-base-lua.postrm 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base-lua.postrm 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,13 @@ +#!/bin/sh +set -e + +if [ purge = "$1" ]; then + # Handle the rare case "userdel" not being available, see bug #1071142. + if [ command -v userdel 1>dev/null 2>&1 ]; then + userdel dnsmasq + fi + rm -rf /run/dnsmasq +fi + +#DEBHELPER# + diff -Nru dnsmasq-2.89/debian/dnsmasq-base.conffiles dnsmasq-2.90/debian/dnsmasq-base.conffiles --- dnsmasq-2.89/debian/dnsmasq-base.conffiles 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base.conffiles 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -/etc/dbus-1/system.d/dnsmasq.conf diff -Nru dnsmasq-2.89/debian/dnsmasq-base.dirs dnsmasq-2.90/debian/dnsmasq-base.dirs --- dnsmasq-2.89/debian/dnsmasq-base.dirs 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base.dirs 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1 @@ +/var/lib/misc diff -Nru dnsmasq-2.89/debian/dnsmasq-base.docs dnsmasq-2.90/debian/dnsmasq-base.docs --- dnsmasq-2.89/debian/dnsmasq-base.docs 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base.docs 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,8 @@ +doc.html +setup.html +dnsmasq.conf.example +FAQ +CHANGELOG.archive +dbus/DBus-interface +debian/systemd_howto +debian/readme diff -Nru dnsmasq-2.89/debian/dnsmasq-base.install dnsmasq-2.90/debian/dnsmasq-base.install --- dnsmasq-2.89/debian/dnsmasq-base.install 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base.install 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,3 @@ +#!/usr/bin/dh-exec +debian/dbus.conf => /usr/share/dbus-1/system.d/dnsmasq.conf +trust-anchors.conf /usr/share/dnsmasq-base diff -Nru dnsmasq-2.89/debian/dnsmasq-base.maintscript dnsmasq-2.90/debian/dnsmasq-base.maintscript --- dnsmasq-2.89/debian/dnsmasq-base.maintscript 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base.maintscript 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,3 @@ +# Due to lintian warning dbus-policy-in-etc this file has been moved to +# /usr/share/dbus-1/system.d/dnsmasq.conf and thus is not a conffile any more. +rm_conffile /etc/dbus-1/system.d/dnsmasq.conf 2.89-1.1~ dnsmasq-base diff -Nru dnsmasq-2.89/debian/dnsmasq-base.postinst dnsmasq-2.90/debian/dnsmasq-base.postinst --- dnsmasq-2.89/debian/dnsmasq-base.postinst 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base.postinst 2024-05-17 15:33:51.000000000 +0000 @@ -2,13 +2,16 @@ set -e # Create the dnsmasq user in dnsmasq-base, so that Dbus doesn't complain. - -# create a user to run as (code stolen from dovecot-common) + if [ "$1" = "configure" ]; then + # Create the user to run as. if [ -z "`id -u dnsmasq 2> /dev/null`" ]; then - adduser --system --home /var/lib/misc --gecos "dnsmasq" \ - --no-create-home --disabled-password \ - --quiet dnsmasq || true + useradd --system \ + --gid nogroup \ + --comment dnsmasq \ + --home-dir /var/lib/misc --no-create-home \ + --shell /usr/sbin/nologin \ + dnsmasq fi # Make the directory where we keep the pid file - this @@ -16,9 +19,12 @@ # This is only actually used by the dnsmasq binary package, not # dnsmasq-base, but it's much easier to create it here so that # we don't have synchronisation issues with the creation of the - # dnsmasq user. + # dnsmasq user. if [ ! -d /run/dnsmasq ]; then mkdir /run/dnsmasq chown dnsmasq:nogroup /run/dnsmasq fi fi + +#DEBHELPER# + diff -Nru dnsmasq-2.89/debian/dnsmasq-base.postrm dnsmasq-2.90/debian/dnsmasq-base.postrm --- dnsmasq-2.89/debian/dnsmasq-base.postrm 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-base.postrm 2024-05-17 15:33:51.000000000 +0000 @@ -2,10 +2,12 @@ set -e if [ purge = "$1" ]; then - if [ -x "$(command -v deluser)" ]; then - deluser --quiet --system dnsmasq > /dev/null || true - else - echo >&2 "not removing dnsmasq system account because deluser command was not found" + # Handle the rare case "userdel" not being available, see bug #1071142. + if [ command -v userdel 1>dev/null 2>&1 ]; then + userdel dnsmasq fi rm -rf /run/dnsmasq fi + +#DEBHELPER# + diff -Nru dnsmasq-2.89/debian/dnsmasq-utils.install dnsmasq-2.90/debian/dnsmasq-utils.install --- dnsmasq-2.89/debian/dnsmasq-utils.install 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-utils.install 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,3 @@ +dhcp_lease_time /usr/bin +dhcp_release /usr/bin +dhcp_release6 /usr/bin diff -Nru dnsmasq-2.89/debian/dnsmasq-utils.manpages dnsmasq-2.90/debian/dnsmasq-utils.manpages --- dnsmasq-2.89/debian/dnsmasq-utils.manpages 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq-utils.manpages 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,3 @@ +dhcp_lease_time.1 +dhcp_release.1 +dhcp_release6.1 diff -Nru dnsmasq-2.89/debian/dnsmasq.default dnsmasq-2.90/debian/dnsmasq.default --- dnsmasq-2.89/debian/dnsmasq.default 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq.default 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,42 @@ +# This file has six functions: +# 1) to completely disable starting this dnsmasq instance +# 2) to set DOMAIN_SUFFIX by running `dnsdomainname` +# 3) to select an alternative config file +# by setting DNSMASQ_OPTS to --conf-file= +# 4) to tell dnsmasq to read the files in /etc/dnsmasq.d for +# more configuration variables. +# 5) to stop the resolvconf package from controlling dnsmasq's +# idea of which upstream nameservers to use. +# 6) to avoid using this dnsmasq instance as the system's default resolver +# by setting DNSMASQ_EXCEPT="lo" +# For upgraders from very old versions, all the shell variables set +# here in previous versions are still honored by the init script +# so if you just keep your old version of this file nothing will break. + +#DOMAIN_SUFFIX=`dnsdomainname` +#DNSMASQ_OPTS="--conf-file=/etc/dnsmasq.alt" + +# The dnsmasq daemon is run by default conforming to the Debian Policy. +# To disable the service, +# for SYSV init, use "update-rc.d dnsmasq disable", +# for systemd, use "systemctl disable dnsmasq". + +# By default search this drop directory for configuration options. +# Libvirt leaves a file here to make the system dnsmasq play nice. +# Comment out this line if you don't want this. The dpkg-* are file +# endings which cause dnsmasq to skip that file. This avoids pulling +# in backups made by dpkg. +CONFIG_DIR=/etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new + +# If the resolvconf package is installed, dnsmasq will use its output +# rather than the contents of /etc/resolv.conf to find upstream +# nameservers. Uncommenting this line inhibits this behaviour. +# Note that including a "resolv-file=" line in +# /etc/dnsmasq.conf is not enough to override resolvconf if it is +# installed: the line below must be uncommented. +#IGNORE_RESOLVCONF=yes + +# If the resolvconf package is installed, dnsmasq will tell resolvconf +# to use dnsmasq under 127.0.0.1 as the system's default resolver. +# Uncommenting this line inhibits this behaviour. +#DNSMASQ_EXCEPT="lo" diff -Nru dnsmasq-2.89/debian/dnsmasq.init dnsmasq-2.90/debian/dnsmasq.init --- dnsmasq-2.89/debian/dnsmasq.init 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq.init 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,170 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: dnsmasq +# Required-Start: $network $remote_fs $syslog +# Required-Stop: $network $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Description: DHCP and DNS server +### END INIT INFO + +# Don't exit on error status +set +e + +# The following test ensures the dnsmasq service is not started, when the +# package 'dnsmasq' is removed but not purged, even if the dnsmasq-base +# package is still in place. +if [ -r /usr/share/dnsmasq/init-system-common ]; then + # 'dnsmasq' is installed: source initial code used also with systemd. + . /usr/share/dnsmasq/init-system-common +else + # 'dnsmasq' is removed but not purged, or damaged: do nothing. + exit 0 +fi + +# Double-check 'dnsmasq-base' or 'dnsmasq-base-lua' is installed. +test -x ${DAEMON} || exit 0 + +# Source the SysV init-functions which should always be available. +. /lib/lsb/init-functions || exit 0 + +start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + + # /run may be volatile, so we need to ensure that + # /run/dnsmasq exists here as well as in postinst + if [ ! -d /run/dnsmasq ]; then + mkdir /run/dnsmasq || { [ -d /run/dnsmasq ] || return 2 ; } + chown dnsmasq:nogroup /run/dnsmasq || return 2 + fi + [ -x /sbin/restorecon ] && /sbin/restorecon /run/dnsmasq + + start-stop-daemon --start --quiet --pidfile /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid --exec ${DAEMON} --test > /dev/null || return 1 + start-stop-daemon --start --quiet --pidfile /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid --exec ${DAEMON} -- \ + -x /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid \ + ${MAILHOSTNAME:+ -m ${MAILHOSTNAME}} \ + ${MAILTARGET:+ -t ${MAILTARGET}} \ + ${DNSMASQ_USER:+ -u ${DNSMASQ_USER}} \ + ${DNSMASQ_INTERFACES:+ ${DNSMASQ_INTERFACES}} \ + ${DHCP_LEASE:+ -l ${DHCP_LEASE}} \ + ${DOMAIN_SUFFIX:+ -s ${DOMAIN_SUFFIX}} \ + ${RESOLV_CONF:+ -r ${RESOLV_CONF}} \ + ${CACHESIZE:+ -c ${CACHESIZE}} \ + ${CONFIG_DIR:+ -7 ${CONFIG_DIR}} \ + ${DNSMASQ_OPTS:+ ${DNSMASQ_OPTS}} \ + || return 2 +} + +stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid --name ${NAME} +} + +status() +{ + # Return + # 0 if daemon is running + # 1 if daemon is dead and pid file exists + # 3 if daemon is not running + # 4 if daemon status is unknown + start-stop-daemon --start --quiet --pidfile /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid --exec ${DAEMON} --test > /dev/null + case "${?}" in + 0) [ -e "/run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid" ] && return 1 ; return 3 ;; + 1) return 0 ;; + *) return 4 ;; + esac +} + +case "${1}" in + start) + log_daemon_msg "Starting ${DESC}" "${NAME}${INSTANCE:+.${INSTANCE}}" + start + case "${?}" in + 0) + log_end_msg 0 + start_resolvconf + exit 0 + ;; + 1) + log_success_msg "(already running)" + exit 0 + ;; + *) + log_end_msg 1 + exit 1 + ;; + esac + ;; + stop) + stop_resolvconf + log_daemon_msg "Stopping ${DESC}" "${NAME}${INSTANCE:+.${INSTANCE}}" + stop + RETVAL="${?}" + case "${RETVAL}" in + 0) log_end_msg 0 ; exit 0 ;; + 1) log_warning_msg "(not running)" ; exit 0 ;; + *) log_end_msg 1; exit 1 ;; + esac + ;; + restart|force-reload) + checkconfig + if [ ${?} -ne 0 ]; then + NAME="configuration syntax check" + RETVAL="2" + else + stop_resolvconf + stop + RETVAL="${?}" + fi + log_daemon_msg "Restarting ${DESC}" "${NAME}${INSTANCE:+.${INSTANCE}}" + case "${RETVAL}" in + 0|1) + sleep 2 + start + case "${?}" in + 0) + log_end_msg 0 + start_resolvconf + exit 0 + ;; + *) + log_end_msg 1 + exit 1 + ;; + esac + ;; + *) + log_end_msg 1 + exit 1 + ;; + esac + ;; + status) + log_daemon_msg "Checking ${DESC}" "${NAME}${INSTANCE:+.${INSTANCE}}" + status + case "${?}" in + 0) log_success_msg "(running)" ; exit 0 ;; + 1) log_success_msg "(dead, pid file exists)" ; exit 1 ;; + 3) log_success_msg "(not running)" ; exit 3 ;; + *) log_success_msg "(unknown)" ; exit 4 ;; + esac + ;; + dump-stats) + kill -s USR1 `cat /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid` + ;; + *) + echo "Usage: /etc/init.d/${NAME} {start|stop|restart|force-reload|dump-stats|status}" >&2 + exit 3 + ;; +esac + +exit 0 diff -Nru dnsmasq-2.89/debian/dnsmasq.install dnsmasq-2.90/debian/dnsmasq.install --- dnsmasq-2.89/debian/dnsmasq.install 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq.install 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,8 @@ +#!/usr/bin/dh-exec +debian/resolvconf => /etc/resolvconf/update.d/dnsmasq +debian/resolvconf-package => /usr/lib/resolvconf/dpkg-event.d/dnsmasq +debian/init-system-common => /usr/share/dnsmasq/init-system-common +debian/systemd-helper => /usr/share/dnsmasq/systemd-helper +dnsmasq.conf.example => /etc/dnsmasq.conf +debian/readme.dnsmasq.d => /etc/dnsmasq.d/README +debian/insserv => /etc/insserv.conf.d/dnsmasq diff -Nru dnsmasq-2.89/debian/dnsmasq.links dnsmasq-2.90/debian/dnsmasq.links --- dnsmasq-2.89/debian/dnsmasq.links 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq.links 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1 @@ +usr/share/dnsmasq-base/trust-anchors.conf usr/share/dnsmasq/trust-anchors.conf diff -Nru dnsmasq-2.89/debian/dnsmasq.maintscript dnsmasq-2.90/debian/dnsmasq.maintscript --- dnsmasq-2.89/debian/dnsmasq.maintscript 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq.maintscript 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,2 @@ +# /usr/share/doc/dnsmasq was a symlink in versions < 2.81-1 (see #985282) +symlink_to_dir /usr/share/doc/dnsmasq dnsmasq-base 2.84-1.2~ dnsmasq diff -Nru dnsmasq-2.89/debian/dnsmasq.runscript/run dnsmasq-2.90/debian/dnsmasq.runscript/run --- dnsmasq-2.89/debian/dnsmasq.runscript/run 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq.runscript/run 2024-05-17 15:33:51.000000000 +0000 @@ -15,14 +15,14 @@ fi # This tells dnsmasq to ignore DNS requests that don't come from a local network. -# It's automatically ignored if --interface --except-interface, --listen-address +# It's automatically ignored if --interface --except-interface, --listen-address # or --auth-server exist in the configuration, so for most installations, it will # have no effect, but for otherwise-unconfigured installations, it stops dnsmasq # from being vulnerable to DNS-reflection attacks. DNSMASQ_OPTS="${DNSMASQ_OPTS:-} --local-service" -# If the dns-root-data package is installed, then the trust anchors will be +# If the dns-root-data package is installed, then the trust anchors will be # available in $ROOT_DS, in BIND zone-file format. Reformat as dnsmasq # --trust-anchor options. diff -Nru dnsmasq-2.89/debian/dnsmasq.service dnsmasq-2.90/debian/dnsmasq.service --- dnsmasq-2.89/debian/dnsmasq.service 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq.service 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,31 @@ +[Unit] +Description=dnsmasq - A lightweight DHCP and caching DNS server +Requires=network.target +Wants=nss-lookup.target +Before=nss-lookup.target +After=network.target + +[Service] +Type=forking +PIDFile=/run/dnsmasq/dnsmasq.pid + +# Test the config file and refuse starting if it is not valid. +ExecStartPre=/usr/share/dnsmasq/systemd-helper checkconfig + +# We run dnsmasq via the /usr/share/dnsmasq/systemd-helper script which acts +# as a wrapper picking up extra configuration files and then execs dnsmasq +# itself, when called with the "exec" function. +ExecStart=/usr/share/dnsmasq/systemd-helper exec + +# The *-resolvconf functions configure (and deconfigure) +# resolvconf to work with the dnsmasq DNS server. They're called like +# this to get correct error handling (ie don't start-resolvconf if the +# dnsmasq daemon fails to start). +ExecStartPost=/usr/share/dnsmasq/systemd-helper start-resolvconf +ExecStop=/usr/share/dnsmasq/systemd-helper stop-resolvconf + + +ExecReload=/bin/kill -HUP $MAINPID + +[Install] +WantedBy=multi-user.target diff -Nru dnsmasq-2.89/debian/dnsmasq.tmpfiles dnsmasq-2.90/debian/dnsmasq.tmpfiles --- dnsmasq-2.89/debian/dnsmasq.tmpfiles 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq.tmpfiles 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1 @@ +d /run/dnsmasq 755 dnsmasq nogroup diff -Nru dnsmasq-2.89/debian/dnsmasq@.service dnsmasq-2.90/debian/dnsmasq@.service --- dnsmasq-2.89/debian/dnsmasq@.service 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/dnsmasq@.service 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,31 @@ +[Unit] +Description=dnsmasq (%i) - A lightweight DHCP and caching DNS server +Requires=network.target +Wants=nss-lookup.target +Before=nss-lookup.target +After=network.target + +[Service] +Type=forking +PIDFile=/run/dnsmasq/dnsmasq.%i.pid + +# Test the config file and refuse starting if it is not valid. +ExecStartPre=/usr/share/dnsmasq/systemd-helper checkconfig "%i" + +# We run dnsmasq via the /usr/share/dnsmasq/systemd-helper script which acts +# as a wrapper picking up extra configuration files and then execs dnsmasq +# itself, when called with the "exec" function. +ExecStart=/usr/share/dnsmasq/systemd-helper exec "%i" + +# The *-resolvconf functions configure (and deconfigure) +# resolvconf to work with the dnsmasq DNS server. They're called like +# this to get correct error handling (ie don't start-resolvconf if the +# dnsmasq daemon fails to start). +ExecStartPost=/usr/share/dnsmasq/systemd-helper start-resolvconf "%i" +ExecStop=/usr/share/dnsmasq/systemd-helper stop-resolvconf "%i" + + +ExecReload=/bin/kill -HUP $MAINPID + +[Install] +WantedBy=multi-user.target diff -Nru dnsmasq-2.89/debian/init dnsmasq-2.90/debian/init --- dnsmasq-2.89/debian/init 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/init 1970-01-01 00:00:00.000000000 +0000 @@ -1,325 +0,0 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: dnsmasq -# Required-Start: $network $remote_fs $syslog -# Required-Stop: $network $remote_fs $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Description: DHCP and DNS server -### END INIT INFO - -# Don't exit on error status -set +e - -PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin -DAEMON=/usr/sbin/dnsmasq -NAME=dnsmasq -DESC="DNS forwarder and DHCP server" -INSTANCE="${2}" - -# Most configuration options in /etc/default/dnsmasq are deprecated -# but still honoured. -ENABLED=1 -if [ -r /etc/default/${NAME}${INSTANCE:+.${INSTANCE}} ]; then - . /etc/default/${NAME}${INSTANCE:+.${INSTANCE}} -fi - -# Get the system locale, so that messages are in the correct language, and the -# charset for IDN is correct -if [ -r /etc/default/locale ]; then - . /etc/default/locale - export LANG -fi - -# The following test ensures the dnsmasq service is not started, when the -# package 'dnsmasq' is removed but not purged, even if the dnsmasq-base -# package is still in place. -test -e /usr/share/dnsmasq/installed-marker || exit 0 - -test -x ${DAEMON} || exit 0 - -# Provide skeleton LSB log functions for backports which don't have LSB functions. -if [ -f /lib/lsb/init-functions ]; then - . /lib/lsb/init-functions -else - log_warning_msg () { - echo "${@}." - } - - log_success_msg () { - echo "${@}." - } - - log_daemon_msg () { - echo -n "${1}: ${2}" - } - - log_end_msg () { - if [ "${1}" -eq 0 ]; then - echo "." - elif [ "${1}" -eq 255 ]; then - /bin/echo -e " (warning)." - else - /bin/echo -e " failed!" - fi - } -fi - -# RESOLV_CONF: -# If the resolvconf package is installed then use the resolv conf file -# that it provides as the default. Otherwise use /etc/resolv.conf as -# the default. -# -# If IGNORE_RESOLVCONF is set in /etc/default/dnsmasq or an explicit -# filename is set there then this inhibits the use of the resolvconf-provided -# information. -# -# Note that if the resolvconf package is installed it is not possible to -# override it just by configuration in /etc/dnsmasq.conf, it is necessary -# to set IGNORE_RESOLVCONF=yes in /etc/default/dnsmasq. - -if [ ! "${RESOLV_CONF}" ] && - [ "${IGNORE_RESOLVCONF}" != "yes" ] && - [ -x /sbin/resolvconf ] -then - RESOLV_CONF=/run/dnsmasq/resolv.conf -fi - -for INTERFACE in ${DNSMASQ_INTERFACE}; do - DNSMASQ_INTERFACES="${DNSMASQ_INTERFACES} -i ${INTERFACE}" -done - -for INTERFACE in ${DNSMASQ_EXCEPT}; do - DNSMASQ_INTERFACES="${DNSMASQ_INTERFACES} -I ${INTERFACE}" -done - -if [ ! "${DNSMASQ_USER}" ]; then - DNSMASQ_USER="dnsmasq" -fi - -# This tells dnsmasq to ignore DNS requests that don't come from a local network. -# It's automatically ignored if --interface --except-interface, --listen-address -# or --auth-server exist in the configuration, so for most installations, it will -# have no effect, but for otherwise-unconfigured installations, it stops dnsmasq -# from being vulnerable to DNS-reflection attacks. - -DNSMASQ_OPTS="${DNSMASQ_OPTS} --local-service" - -# If the dns-root-data package is installed, then the trust anchors will be -# available in ROOT_DS, in BIND zone-file format. Reformat as dnsmasq -# --trust-anchor options. - -ROOT_DS="/usr/share/dns/root.ds" - -if [ -f ${ROOT_DS} ]; then - DNSMASQ_OPTS="$DNSMASQ_OPTS `env LC_ALL=C sed -rne "s/^([.a-zA-Z0-9]+)([[:space:]]+[0-9]+)*([[:space:]]+IN)*[[:space:]]+DS[[:space:]]+/--trust-anchor=\1,/;s/[[:space:]]+/,/gp" $ROOT_DS | tr '\n' ' '`" -fi - -start() -{ - # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started - - # /run may be volatile, so we need to ensure that - # /run/dnsmasq exists here as well as in postinst - if [ ! -d /run/dnsmasq ]; then - mkdir /run/dnsmasq || { [ -d /run/dnsmasq ] || return 2 ; } - chown dnsmasq:nogroup /run/dnsmasq || return 2 - fi - [ -x /sbin/restorecon ] && /sbin/restorecon /run/dnsmasq - - start-stop-daemon --start --quiet --pidfile /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid --exec ${DAEMON} --test > /dev/null || return 1 - start-stop-daemon --start --quiet --pidfile /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid --exec ${DAEMON} -- \ - -x /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid \ - ${MAILHOSTNAME:+ -m ${MAILHOSTNAME}} \ - ${MAILTARGET:+ -t ${MAILTARGET}} \ - ${DNSMASQ_USER:+ -u ${DNSMASQ_USER}} \ - ${DNSMASQ_INTERFACES:+ ${DNSMASQ_INTERFACES}} \ - ${DHCP_LEASE:+ -l ${DHCP_LEASE}} \ - ${DOMAIN_SUFFIX:+ -s ${DOMAIN_SUFFIX}} \ - ${RESOLV_CONF:+ -r ${RESOLV_CONF}} \ - ${CACHESIZE:+ -c ${CACHESIZE}} \ - ${CONFIG_DIR:+ -7 ${CONFIG_DIR}} \ - ${DNSMASQ_OPTS:+ ${DNSMASQ_OPTS}} \ - || return 2 -} - -start_resolvconf() -{ -# If interface "lo" is explicitly disabled in /etc/default/dnsmasq -# Then dnsmasq won't be providing local DNS, so don't add it to -# the resolvconf server set. - for interface in ${DNSMASQ_EXCEPT}; do - [ ${interface} = lo ] && return - done - - # Also skip this if DNS functionality is disabled in /etc/dnsmasq.conf - if grep -qs '^port=0' /etc/dnsmasq.conf; then - return - fi - - if [ -x /sbin/resolvconf ] ; then - echo "nameserver 127.0.0.1" | /sbin/resolvconf -a lo.${NAME}${INSTANCE:+.${INSTANCE}} - fi - return 0 -} - -stop() -{ - # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid --name ${NAME} -} - -stop_resolvconf() -{ - if [ -x /sbin/resolvconf ] ; then - /sbin/resolvconf -d lo.${NAME}${INSTANCE:+.${INSTANCE}} - fi - return 0 -} - -status() -{ - # Return - # 0 if daemon is running - # 1 if daemon is dead and pid file exists - # 3 if daemon is not running - # 4 if daemon status is unknown - start-stop-daemon --start --quiet --pidfile /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid --exec ${DAEMON} --test > /dev/null - case "${?}" in - 0) [ -e "/run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid" ] && return 1 ; return 3 ;; - 1) return 0 ;; - *) return 4 ;; - esac -} - -case "${1}" in - start) - test "${ENABLED}" != "0" || exit 0 - log_daemon_msg "Starting ${DESC}" "${NAME}${INSTANCE:+.${INSTANCE}}" - start - case "${?}" in - 0) - log_end_msg 0 - start_resolvconf - exit 0 - ;; - 1) - log_success_msg "(already running)" - exit 0 - ;; - *) - log_end_msg 1 - exit 1 - ;; - esac - ;; - stop) - stop_resolvconf - if [ "${ENABLED}" != "0" ]; then - log_daemon_msg "Stopping ${DESC}" "${NAME}${INSTANCE:+.${INSTANCE}}" - fi - stop - RETVAL="${?}" - if [ "${ENABLED}" = "0" ]; then - case "${RETVAL}" in - 0) log_daemon_msg "Stopping ${DESC}" "${NAME}${INSTANCE:+.${INSTANCE}}"; log_end_msg 0 ;; - esac - exit 0 - fi - case "${RETVAL}" in - 0) log_end_msg 0 ; exit 0 ;; - 1) log_warning_msg "(not running)" ; exit 0 ;; - *) log_end_msg 1; exit 1 ;; - esac - ;; - checkconfig) - ${DAEMON} --test ${CONFIG_DIR:+ -7 ${CONFIG_DIR}} ${DNSMASQ_OPTS:+ ${DNSMASQ_OPTS}} >/dev/null 2>&1 - RETVAL="${?}" - exit ${RETVAL} - ;; - restart|force-reload) - test "${ENABLED}" != "0" || exit 1 - ${DAEMON} --test ${CONFIG_DIR:+ -7 ${CONFIG_DIR}} ${DNSMASQ_OPTS:+ ${DNSMASQ_OPTS}} >/dev/null 2>&1 - if [ ${?} -ne 0 ]; then - NAME="configuration syntax check" - RETVAL="2" - else - stop_resolvconf - stop - RETVAL="${?}" - fi - log_daemon_msg "Restarting ${DESC}" "${NAME}${INSTANCE:+.${INSTANCE}}" - case "${RETVAL}" in - 0|1) - sleep 2 - start - case "${?}" in - 0) - log_end_msg 0 - start_resolvconf - exit 0 - ;; - *) - log_end_msg 1 - exit 1 - ;; - esac - ;; - *) - log_end_msg 1 - exit 1 - ;; - esac - ;; - status) - log_daemon_msg "Checking ${DESC}" "${NAME}${INSTANCE:+.${INSTANCE}}" - status - case "${?}" in - 0) log_success_msg "(running)" ; exit 0 ;; - 1) log_success_msg "(dead, pid file exists)" ; exit 1 ;; - 3) log_success_msg "(not running)" ; exit 3 ;; - *) log_success_msg "(unknown)" ; exit 4 ;; - esac - ;; - dump-stats) - kill -s USR1 `cat /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid` - ;; - systemd-start-resolvconf) - start_resolvconf - ;; - systemd-stop-resolvconf) - stop_resolvconf - ;; - systemd-exec) - # /run may be volatile, so we need to ensure that - # /run/dnsmasq exists here as well as in postinst - if [ ! -d /run/dnsmasq ]; then - mkdir /run/dnsmasq || { [ -d /run/dnsmasq ] || return 2 ; } - chown dnsmasq:nogroup /run/dnsmasq || return 2 - fi - exec ${DAEMON} -x /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid \ - ${MAILHOSTNAME:+ -m ${MAILHOSTNAME}} \ - ${MAILTARGET:+ -t ${MAILTARGET}} \ - ${DNSMASQ_USER:+ -u ${DNSMASQ_USER}} \ - ${DNSMASQ_INTERFACES:+ ${DNSMASQ_INTERFACES}} \ - ${DHCP_LEASE:+ -l ${DHCP_LEASE}} \ - ${DOMAIN_SUFFIX:+ -s ${DOMAIN_SUFFIX}} \ - ${RESOLV_CONF:+ -r ${RESOLV_CONF}} \ - ${CACHESIZE:+ -c ${CACHESIZE}} \ - ${CONFIG_DIR:+ -7 ${CONFIG_DIR}} \ - ${DNSMASQ_OPTS:+ ${DNSMASQ_OPTS}} - ;; - *) - echo "Usage: /etc/init.d/${NAME} {start|stop|restart|force-reload|dump-stats|status}" >&2 - exit 3 - ;; -esac - -exit 0 diff -Nru dnsmasq-2.89/debian/init-system-common dnsmasq-2.90/debian/init-system-common --- dnsmasq-2.89/debian/init-system-common 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/init-system-common 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,102 @@ +# -*- shell-script -*- +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/sbin/dnsmasq +NAME=dnsmasq +DESC="DNS forwarder and DHCP server" +INSTANCE="${2}" + +# Most configuration options in /etc/default/dnsmasq are deprecated +# but still honoured. +if [ -r /etc/default/${NAME}${INSTANCE:+.${INSTANCE}} ]; then + . /etc/default/${NAME}${INSTANCE:+.${INSTANCE}} +fi + +# Get the system locale, so that messages are in the correct language, and the +# charset for IDN is correct +if [ -r /etc/default/locale ]; then + . /etc/default/locale + export LANG +fi + +# RESOLV_CONF: +# If the resolvconf package is installed then use the resolv conf file +# that it provides as the default. Otherwise use /etc/resolv.conf as +# the default. +# +# If IGNORE_RESOLVCONF is set in /etc/default/dnsmasq or an explicit +# filename is set there then this inhibits the use of the resolvconf-provided +# information. +# +# Note that if the resolvconf package is installed it is not possible to +# override it just by configuration in /etc/dnsmasq.conf, it is necessary +# to set IGNORE_RESOLVCONF=yes in /etc/default/dnsmasq. + +if [ ! "${RESOLV_CONF}" ] && + [ "${IGNORE_RESOLVCONF}" != "yes" ] && + [ -x /sbin/resolvconf ] +then + RESOLV_CONF=/run/dnsmasq/resolv.conf +fi + +for INTERFACE in ${DNSMASQ_INTERFACE}; do + DNSMASQ_INTERFACES="${DNSMASQ_INTERFACES} -i ${INTERFACE}" +done + +for INTERFACE in ${DNSMASQ_EXCEPT}; do + DNSMASQ_INTERFACES="${DNSMASQ_INTERFACES} -I ${INTERFACE}" +done + +if [ ! "${DNSMASQ_USER}" ]; then + DNSMASQ_USER="dnsmasq" +fi + +# This tells dnsmasq to ignore DNS requests that don't come from a local network. +# It's automatically ignored if --interface --except-interface, --listen-address +# or --auth-server exist in the configuration, so for most installations, it will +# have no effect, but for otherwise-unconfigured installations, it stops dnsmasq +# from being vulnerable to DNS-reflection attacks. + +DNSMASQ_OPTS="${DNSMASQ_OPTS} --local-service" + +# If the dns-root-data package is installed, then the trust anchors will be +# available in ROOT_DS, in BIND zone-file format. Reformat as dnsmasq +# --trust-anchor options. + +ROOT_DS="/usr/share/dns/root.ds" + +if [ -f ${ROOT_DS} ]; then + DNSMASQ_OPTS="$DNSMASQ_OPTS `env LC_ALL=C sed -rne "s/^([.a-zA-Z0-9]+)([[:space:]]+[0-9]+)*([[:space:]]+IN)*[[:space:]]+DS[[:space:]]+/--trust-anchor=\1,/;s/[[:space:]]+/,/gp" $ROOT_DS | tr '\n' ' '`" +fi + +checkconfig() +{ + ${DAEMON} --test ${CONFIG_DIR:+ -7 ${CONFIG_DIR}} ${DNSMASQ_OPTS:+ ${DNSMASQ_OPTS}} >/dev/null 2>&1 +} + +start_resolvconf() +{ +# If interface "lo" is explicitly disabled in /etc/default/dnsmasq +# Then dnsmasq won't be providing local DNS, so don't add it to +# the resolvconf server set. + for interface in ${DNSMASQ_EXCEPT}; do + [ ${interface} = lo ] && return + done + + # Also skip this if DNS functionality is disabled in /etc/dnsmasq.conf + if grep -qs '^port=0' /etc/dnsmasq.conf; then + return + fi + + if [ -x /sbin/resolvconf ] ; then + echo "nameserver 127.0.0.1" | /sbin/resolvconf -a lo.${NAME}${INSTANCE:+.${INSTANCE}} + fi + return 0 +} + +stop_resolvconf() +{ + if [ -x /sbin/resolvconf ] ; then + /sbin/resolvconf -d lo.${NAME}${INSTANCE:+.${INSTANCE}} + fi + return 0 +} diff -Nru dnsmasq-2.89/debian/installed-marker dnsmasq-2.90/debian/installed-marker --- dnsmasq-2.89/debian/installed-marker 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/installed-marker 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -# This file indicates dnsmasq (and not just dnsmasq-base) is installed. -# It is an implementation detail of the dnsmasq init script. diff -Nru dnsmasq-2.89/debian/lintian-override dnsmasq-2.90/debian/lintian-override --- dnsmasq-2.89/debian/lintian-override 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/lintian-override 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -# dnsmasq-base and dnsmasq-base-lua are mutually exclusive and both -# provide /usr/share/doc/dnsmasq-base -dnsmasq-base-lua binary: usr-share-doc-symlink-without-dependency dnsmasq-base diff -Nru dnsmasq-2.89/debian/patches/eliminate-privacy-breaches.patch dnsmasq-2.90/debian/patches/eliminate-privacy-breaches.patch --- dnsmasq-2.89/debian/patches/eliminate-privacy-breaches.patch 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/patches/eliminate-privacy-breaches.patch 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,40 @@ +Description: Remove or replace privacy breaching logos and forms + Lintian complains about these by issuing the tags privacy-breach-logo and + privacy-breach-donation. +Forwarded: not-needed +Author: Sven Geuer +Last-Update: 2023-11-18 + +--- a/doc.html ++++ b/doc.html +@@ -1,14 +1,11 @@ + + + Dnsmasq - network services for small networks. +- + + + + +- + +- +

Dnsmasq

+ Dnsmasq provides network infrastructure for small networks: DNS, DHCP, router advertisement and network boot. It is designed to be + lightweight and have a small footprint, suitable for resource constrained routers and firewalls. It has also been widely used +@@ -88,14 +85,6 @@ + Dnsmasq is mainly written and maintained by Simon Kelley. For most of its life, dnsmasq has been a spare-time project. + These days I'm working on it as my main activity. + I don't have an employer or anyone who pays me regularly to work on dnsmasq. If you'd like to make +-a contribution towards my expenses, please use the donation button below. +-
+- +- +- +- +-
+- +- ++a contribution towards my expenses, please use the donation button at the project's home page. + + diff -Nru dnsmasq-2.89/debian/patches/series dnsmasq-2.90/debian/patches/series --- dnsmasq-2.89/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/patches/series 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1 @@ +eliminate-privacy-breaches.patch diff -Nru dnsmasq-2.89/debian/postinst dnsmasq-2.90/debian/postinst --- dnsmasq-2.89/debian/postinst 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/postinst 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -#!/bin/sh -set -e - -# /usr/share/doc/dnsmasq was a symlink in versions < 2.81-1 (see #985282) -dpkg-maintscript-helper symlink_to_dir /usr/share/doc/dnsmasq dnsmasq-base 2.84-1.2~ dnsmasq -- "$@" - -# Code copied from dh_systemd_enable ---------------------- -# This will only remove masks created by d-s-h on package removal. -deb-systemd-helper unmask dnsmasq.service >/dev/null || true - -# was-enabled defaults to true, so new installations run enable. -if deb-systemd-helper --quiet was-enabled dnsmasq.service; then - # Enables the unit on first installation, creates new - # symlinks on upgrades if the unit file has changed. - deb-systemd-helper enable dnsmasq.service >/dev/null || true -else - # Update the statefile to add new symlinks (if any), which need to be - # cleaned up on purge. Also remove old symlinks. - deb-systemd-helper update-state dnsmasq.service >/dev/null || true -fi -# End code copied from dh_systemd_enable ------------------ - -if [ -x /etc/init.d/dnsmasq ]; then - update-rc.d dnsmasq defaults 15 85 >/dev/null - - if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ]; then - if [ -e /run/dnsmasq/dnsmasq.pid ]; then - ACTION=restart - else - ACTION=start - fi - - if [ -x /usr/sbin/invoke-rc.d ] ; then - invoke-rc.d dnsmasq $ACTION || true - else - /etc/init.d/dnsmasq $ACTION || true - fi - fi -fi - - diff -Nru dnsmasq-2.89/debian/postrm dnsmasq-2.90/debian/postrm --- dnsmasq-2.89/debian/postrm 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/postrm 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -#!/bin/sh -set -e - -# /usr/share/doc/dnsmasq was a symlink in versions < 2.81-1 (see #985282) -dpkg-maintscript-helper symlink_to_dir /usr/share/doc/dnsmasq dnsmasq-base 2.84-1.2~ dnsmasq -- "$@" - -if [ purge = "$1" ]; then - update-rc.d dnsmasq remove >/dev/null -fi - -# Code copied from dh_systemd_enable ---------------------- -if [ "$1" = "remove" ]; then - if [ -x "/usr/bin/deb-systemd-helper" ]; then - deb-systemd-helper mask dnsmasq.service >/dev/null - fi -fi - -if [ "$1" = "purge" ]; then - if [ -x "/usr/bin/deb-systemd-helper" ]; then - deb-systemd-helper purge dnsmasq.service >/dev/null - deb-systemd-helper unmask dnsmasq.service >/dev/null - fi -fi -# End code copied from dh_systemd_enable ------------------ - diff -Nru dnsmasq-2.89/debian/preinst dnsmasq-2.90/debian/preinst --- dnsmasq-2.89/debian/preinst 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/preinst 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -#!/bin/sh -set -e - -# /usr/share/doc/dnsmasq was a symlink in versions < 2.81-1 (see #985282) -dpkg-maintscript-helper symlink_to_dir /usr/share/doc/dnsmasq dnsmasq-base 2.84-1.2~ dnsmasq -- "$@" diff -Nru dnsmasq-2.89/debian/prerm dnsmasq-2.90/debian/prerm --- dnsmasq-2.89/debian/prerm 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/prerm 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -#!/bin/sh -set -e - -if [ "$1" = "remove" ]; then - if [ -x /usr/sbin/invoke-rc.d ] ; then - invoke-rc.d dnsmasq stop || true - else - /etc/init.d/dnsmasq stop || true - fi -fi - -exit 0 - - diff -Nru dnsmasq-2.89/debian/readme dnsmasq-2.90/debian/readme --- dnsmasq-2.89/debian/readme 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/readme 2024-05-17 15:33:51.000000000 +0000 @@ -4,13 +4,13 @@ commented; see also the dnsmasq.8 man page for explanation of the options. The file /etc/default/dnsmasq also exists but it shouldn't need to be touched in most cases. To set up DHCP - options you might need to refer to a copy of RFC 2132. This is + options you might need to refer to a copy of RFC 2132. This is available on Debian systems in the package doc-rfc-std as the file /usr/share/doc/RFC/draft-standard/rfc2132.txt.gz . (2) Installing the dnsmasq package also creates the directory /etc/dnsmasq.d which is searched by dnsmasq for configuration file - fragments. This behaviour can be disabled by editing + fragments. This behaviour can be disabled by editing /etc/default/dnsmasq. (3) If the Debian resolvconf package is installed then, regardless @@ -30,25 +30,25 @@ generated file /etc/ppp/resolv.conf. You should list 127.0.0.1 as the first nameserver address in /etc/resolv.conf. -(6) In the absence of resolvconf, dns-nameservers lines in +(6) In the absence of resolvconf, dns-nameservers lines in /etc/network/interfaces are ignored. If you do not use resolvconf, list 127.0.0.1 as the first nameserver address in /etc/resolv.conf and configure your nameservers using "server=" lines in /etc/dnsmasq.conf. (7) If you run multiple DNS servers on a single machine, each - listening on a different interface, then it is necessary to use - the bind-interfaces option by uncommenting "bind-interfaces" in - /etc/dnsmasq.conf. This option stops dnsmasq from binding the + listening on a different interface, then it is necessary to use + the bind-interfaces option by uncommenting "bind-interfaces" in + /etc/dnsmasq.conf. This option stops dnsmasq from binding the wildcard address and allows servers listening on port 53 on - interfaces not in use by dnsmasq to work. The Debian + interfaces not in use by dnsmasq to work. The Debian libvirt package will add a configuration file in /etc/dnsmasq.d which does this so that the "system" dnsmasq and "private" dnsmasq instances started by libvirt do not clash. (8) The following options are supported in DEB_BUILD_OPTIONS noopt : compile without optimisation. - nostrip : don't remove symbols from binary. + nostrip : don't remove symbols from binary. nodocs : omit documentation. notftp : omit TFTP support. nodhcp : omit DHCP support. @@ -58,7 +58,7 @@ in Lua. noipv6 : omit IPv6 support. nodbus : omit DBus support. - noconntrack : omit connection tracking support. + noconntrack : omit connection tracking support. noipset : omit IPset support. nonftset : omit nftset support. nortc : compile alternate mode suitable for systems without an RTC. @@ -67,7 +67,7 @@ combined with noi18n to be effective. gitversion : set the version of the produced packages from the git-derived versioning information on the source, - rather than the debian changelog. + rather than the debian changelog. (9) Dnsmasq comes as three packages - dnsmasq-utils, dnsmasq-base and dnsmasq. Dnsmasq-base provides the dnsmasq executable and @@ -76,5 +76,5 @@ infrastructure. This file assumes that both are installed. It is possible to install only dnsmasq-base and use dnsmasq as a non-"system" daemon. Libvirt, for instance, does this. - Dnsmasq-utils provides the utilities dhcp_release and + Dnsmasq-utils provides the utilities dhcp_release and dhcp_lease_time. diff -Nru dnsmasq-2.89/debian/resolvconf dnsmasq-2.90/debian/resolvconf --- dnsmasq-2.89/debian/resolvconf 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/resolvconf 2024-05-17 15:33:51.000000000 +0000 @@ -48,7 +48,7 @@ for F in $(/lib/resolvconf/list-records --after "lo.$MY_NAME_FOR_RESOLVCONF") ; do case "$F" in "lo.$MY_NAME_FOR_RESOLVCONF") - # Omit own record + # Omit own record ;; lo.*) # Include no more records after one for a local nameserver diff -Nru dnsmasq-2.89/debian/rules dnsmasq-2.90/debian/rules --- dnsmasq-2.89/debian/rules 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/rules 2024-05-17 15:33:51.000000000 +0000 @@ -1,309 +1,127 @@ #!/usr/bin/make -f -# debian/rules file - for dnsmasq. -# Copyright 2001-2020 by Simon Kelley -# Based on the sample in the debian hello package which carries the following: -# Copyright 1994,1995 by Ian Jackson. -# I hereby give you perpetual unlimited permission to copy, -# modify and relicense this file, provided that you do not remove -# my name from the file itself. (I assert my moral right of -# paternity under the Copyright, Designs and Patents Act 1988.) -# This file may have to be extensively modified +# -*- makefile -*- -package=dnsmasq-base +# Uncomment this to turn on verbose mode. +export DH_VERBOSE=1 -dpkg_buildflags := DEB_BUILD_MAINT_OPTIONS="hardening=+all,+pie,+bindnow" dpkg-buildflags +# Make sure lintian does not complain about missing hardenings. +export DEB_BUILD_MAINT_OPTIONS = hardening=+all -CFLAGS = $(shell $(dpkg_buildflags) --get CFLAGS) -CFLAGS += $(shell $(dpkg_buildflags) --get CPPFLAGS) -CFLAGS += -Wall -W +include /usr/share/dpkg/architecture.mk -LDFLAGS = $(shell $(dpkg_buildflags) --get LDFLAGS) - -DEB_COPTS = $(COPTS) - -TARGET = install-i18n - -DEB_HOST_ARCH_OS := $(shell dpkg-architecture -qDEB_HOST_ARCH_OS) -DEB_HOST_GNU_TYPE := $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) -DEB_BUILD_GNU_TYPE := $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) -BUILD_DATE := $(shell dpkg-parsechangelog --show-field Date) - -ifeq ($(origin CC),default) - CC = $(DEB_HOST_GNU_TYPE)-gcc -endif - -# Support non-cross-builds on systems without gnu-triplet-binaries for pkg-config. -ifeq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE)) - PKG_CONFIG=pkg-config -else - PKG_CONFIG=$(DEB_HOST_GNU_TYPE)-pkg-config -endif - -# Force package version based on git tags. -ifneq (,$(filter gitversion,$(DEB_BUILD_OPTIONS))) - PACKAGE_VERSION = $(shell bld/get-version `pwd` | sed 's/test/~&/; s/[a-z]/~&/; s/-/./g; s/$$/-1/; s/^/-v/';) -endif +PREFIX = /usr +# Upstream does not handle CPPFLAGS, so we add it to CFLAGS here. +CFLAGS += $(CPPFLAGS) +COPTS = ifeq (,$(filter nodbus,$(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DHAVE_DBUS + COPTS += -DHAVE_DBUS endif ifeq (,$(filter noidn, $(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DHAVE_LIBIDN2 + COPTS += -DHAVE_LIBIDN2 endif ifeq (,$(filter nonftset, $(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DHAVE_NFTSET + COPTS += -DHAVE_NFTSET endif ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS))) ifeq ($(DEB_HOST_ARCH_OS),linux) - DEB_COPTS += -DHAVE_CONNTRACK + COPTS += -DHAVE_CONNTRACK endif endif ifneq (,$(filter noipset,$(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DNO_IPSET + COPTS += -DNO_IPSET endif ifneq (,$(filter nodhcp6,$(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DNO_DHCP6 + COPTS += -DNO_DHCP6 endif ifneq (,$(filter noipv6,$(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DNO_IPV6 + COPTS += -DNO_IPV6 endif ifneq (,$(filter notftp,$(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DNO_TFTP + COPTS += -DNO_TFTP endif ifneq (,$(filter nodhcp,$(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DNO_DHCP + COPTS += -DNO_DHCP endif ifneq (,$(filter noscript,$(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DNO_SCRIPT + COPTS += -DNO_SCRIPT endif ifneq (,$(filter nortc,$(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DHAVE_BROKEN_RTC -endif - -ifneq (,$(filter noi18n,$(DEB_BUILD_OPTIONS))) - TARGET = install -endif - -ifneq (,$(filter uselua,$(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DHAVE_LUASCRIPT + COPTS += -DHAVE_BROKEN_RTC endif ifeq (,$(filter nodnssec,$(DEB_BUILD_OPTIONS))) - DEB_COPTS += -DHAVE_DNSSEC + COPTS += -DHAVE_DNSSEC endif -ifeq ($(DEB_HOST_ARCH_OS),kfreebsd) - # For strlcpy in FreeBSD - LIBS += $(shell ${PKG_CONFIG} --libs libbsd-overlay) - CFLAGS += $(shell ${PKG_CONFIG} --cflags libbsd-overlay) -endif - -define build_tree - rm -rf $1 - install -m 755 \ - -d $1/DEBIAN \ - -d $1/etc/dbus-1/system.d \ - -d $1/usr/share/doc/$(package) \ - -d $1/usr/share/doc/$(package)/examples \ - -d $1/usr/share/$(package) \ - -d $1/var/lib/misc - -endef - -define add_docs -# Need to remove paypal links in Debian Package for policy reasons. - sed -e /\Donations/Q -e /icon.png/d doc.html -e /favicon.ico/d >$1/usr/share/doc/$(package)/doc.html - echo "" >>$1/usr/share/doc/$(package)/doc.html - install -m 644 setup.html $1/usr/share/doc/$(package)/. - install -m 644 dnsmasq.conf.example $1/usr/share/doc/$(package)/examples/. - install -m 644 FAQ $1/usr/share/doc/$(package)/. - gzip -9n $1/usr/share/doc/$(package)/FAQ - install -m 644 CHANGELOG $1/usr/share/doc/$(package)/changelog - gzip -9n $1/usr/share/doc/$(package)/changelog - install -m 644 CHANGELOG.archive $1/usr/share/doc/$(package)/changelog.archive - gzip -9n $1/usr/share/doc/$(package)/changelog.archive - install -m 644 dbus/DBus-interface $1/usr/share/doc/$(package)/. - gzip -9n $1/usr/share/doc/$(package)/DBus-interface - install -m 644 debian/systemd_howto $1/usr/share/doc/$(package)/. - gzip -9n $1/usr/share/doc/$(package)/systemd_howto - gzip -9n $1/usr/share/man/man8/dnsmasq.8 - for f in $1/usr/share/man/*; do \ - if [ -f $$f/man8/dnsmasq.8 ]; then \ - gzip -9n $$f/man8/dnsmasq.8 ; \ - fi \ - done -endef -define add_files - install -m 644 trust-anchors.conf $1/usr/share/$(package)/. - install -m 644 debian/dnsmasq-base.conffiles $1/DEBIAN/conffiles - install -m 755 debian/dnsmasq-base.postinst $1/DEBIAN/postinst - install -m 755 debian/dnsmasq-base.postrm $1/DEBIAN/postrm - install -m 644 debian/changelog $1/usr/share/doc/$(package)/changelog.Debian - gzip -9n $1/usr/share/doc/$(package)/changelog.Debian - install -m 644 debian/readme $1/usr/share/doc/$(package)/README.Debian - install -m 644 debian/copyright $1/usr/share/doc/$(package)/copyright - install -m 644 debian/dbus.conf $1/etc/dbus-1/system.d/dnsmasq.conf -endef - -clean: - $(checkdir) - make BUILDDIR=debian/build/no-lua clean - make BUILDDIR=debian/build/lua clean - make -C contrib/lease-tools clean - rm -rf debian/build debian/trees debian/*~ debian/files debian/substvars debian/utils-substvars - -binary-indep: checkroot - $(checkdir) - rm -rf debian/trees/daemon - install -m 755 \ - -d debian/trees/daemon/DEBIAN \ - -d debian/trees/daemon/usr/share/doc/dnsmasq \ - -d debian/trees/daemon/etc/init.d \ - -d debian/trees/daemon/etc/dnsmasq.d \ - -d debian/trees/daemon/etc/resolvconf/update.d \ - -d debian/trees/daemon/usr/lib/resolvconf/dpkg-event.d \ - -d debian/trees/daemon/usr/share/dnsmasq \ - -d debian/trees/daemon/usr/share/doc/dnsmasq \ - -d debian/trees/daemon/etc/default \ - -d debian/trees/daemon/lib/systemd/system \ - -d debian/trees/daemon/usr/lib/tmpfiles.d \ - -d debian/trees/daemon/etc/insserv.conf.d - install -m 644 debian/conffiles debian/trees/daemon/DEBIAN - install -m 755 debian/postinst debian/postrm debian/preinst debian/prerm debian/trees/daemon/DEBIAN - if ! dpkg-vendor --derives-from Ubuntu; then \ - rm -f debian/dnsmasq.postinst.debhelper debian/dnsmasq.postrm.debhelper; \ - dh_runit -pdnsmasq -Pdebian/trees/daemon; \ - cat debian/dnsmasq.postinst.debhelper >> debian/trees/daemon/DEBIAN/postinst; \ - cat debian/dnsmasq.postrm.debhelper >> debian/trees/daemon/DEBIAN/postrm; \ - cd debian/trees/daemon && find etc/sv -type f -printf '/%p\n' >>DEBIAN/conffiles; \ +%: + # Ubuntu and derivates do not support runit, see + # https://bugs.debian.org/960401 for details. + if dpkg-vendor --derives-from Ubuntu; then \ + dh $@; \ + else \ + dh $@ --with runit; \ fi - install -m 755 debian/init debian/trees/daemon/etc/init.d/dnsmasq - install -m 755 debian/resolvconf debian/trees/daemon/etc/resolvconf/update.d/dnsmasq - install -m 755 debian/resolvconf-package debian/trees/daemon/usr/lib/resolvconf/dpkg-event.d/dnsmasq - install -m 644 debian/installed-marker debian/trees/daemon/usr/share/dnsmasq - install -m 644 debian/default debian/trees/daemon/etc/default/dnsmasq - install -m 644 dnsmasq.conf.example debian/trees/daemon/etc/dnsmasq.conf - install -m 644 debian/readme.dnsmasq.d debian/trees/daemon/etc/dnsmasq.d/README - install -m 644 debian/systemd.service debian/trees/daemon/lib/systemd/system/dnsmasq.service - install -m 644 debian/systemd@.service debian/trees/daemon/lib/systemd/system/dnsmasq@.service - install -m 644 debian/tmpfiles.conf debian/trees/daemon/usr/lib/tmpfiles.d/dnsmasq.conf - install -m 644 debian/insserv debian/trees/daemon/etc/insserv.conf.d/dnsmasq - install -m 644 debian/copyright debian/trees/daemon/usr/share/doc/dnsmasq/copyright - install -m 644 debian/changelog debian/trees/daemon/usr/share/doc/dnsmasq/changelog.Debian - gzip -9n debian/trees/daemon/usr/share/doc/dnsmasq/changelog.Debian - cd debian/trees/daemon && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums - dpkg-gencontrol $(PACKAGE_VERSION) -Tdebian/dnsmasq.substvars -pdnsmasq -Pdebian/trees/daemon - find debian/trees/daemon -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)' - chown -R root.root debian/trees/daemon - chmod -R g-ws debian/trees/daemon - dpkg --build debian/trees/daemon .. - -binary-arch: checkroot - $(call build_tree,debian/trees/base) - make $(TARGET) BUILDDIR=debian/build/no-lua PREFIX=/usr DESTDIR=`pwd`/debian/trees/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=$(CC) PKG_CONFIG=$(PKG_CONFIG) LIBS="$(LIBS)" -ifeq (,$(findstring nodoc,$(DEB_BUILD_OPTIONS))) - $(call add_docs,debian/trees/base) -else - rm -rf debian/trees/base/usr/share/man -endif - $(call add_files,debian/trees/base) -ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) - $(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/trees/base/usr/sbin/dnsmasq -endif - cd debian/trees/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums - dpkg-shlibdeps --warnings=1 debian/trees/base/usr/sbin/dnsmasq - dpkg-gencontrol $(PACKAGE_VERSION) -pdnsmasq-base -Pdebian/trees/base - find debian/trees/base -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)' - chown -R root.root debian/trees/base - chmod -R g-ws debian/trees/base - dpkg --build debian/trees/base .. - - $(call build_tree,debian/trees/lua-base) - make $(TARGET) BUILDDIR=debian/build/lua PREFIX=/usr DESTDIR=`pwd`/debian/trees/lua-base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="-DHAVE_LUASCRIPT $(DEB_COPTS)" CC=$(CC) PKG_CONFIG=$(PKG_CONFIG) LIBS="$(LIBS)" -ifeq (,$(findstring nodoc,$(DEB_BUILD_OPTIONS))) - $(call add_docs,debian/trees/lua-base) -else - rm -rf debian/trees/lua-base/usr/share/man -endif - $(call add_files,debian/trees/lua-base) - install -m 755 -d debian/trees/lua-base/usr/share/lintian/overrides - install -m 644 debian/lintian-override debian/trees/lua-base/usr/share/lintian/overrides/dnsmasq-base-lua -ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) - $(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/trees/lua-base/usr/sbin/dnsmasq -endif - ln -s $(package) debian/trees/lua-base/usr/share/doc/dnsmasq-base-lua - cd debian/trees/lua-base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums - dpkg-shlibdeps --warnings=1 debian/trees/lua-base/usr/sbin/dnsmasq - dpkg-gencontrol $(PACKAGE_VERSION) -pdnsmasq-base-lua -Pdebian/trees/lua-base - find debian/trees/lua-base -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)' - chown -R root.root debian/trees/lua-base - chmod -R g-ws debian/trees/lua-base - dpkg --build debian/trees/lua-base .. +# Upstream builds and installs in one go, so do we. +override_dh_auto_build: -ifeq ($(DEB_HOST_ARCH_OS),linux) - rm -rf debian/trees/utils - install -m 755 -d debian/trees/utils/DEBIAN \ - -d debian/trees/utils/usr/bin \ - -d debian/trees/utils/usr/share/doc/dnsmasq-utils +override_dh_auto_install: + dh_auto_build -p dnsmasq-base --no-parallel -- install-i18n \ + BUILDDIR=debian/auto-build/dnsmasq-base \ + DESTDIR=$(CURDIR)/debian/dnsmasq-base \ + PREFIX=$(PREFIX) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + COPTS="$(COPTS)" + dh_auto_build -p dnsmasq-base-lua --no-parallel -- install-i18n \ + BUILDDIR=debian/auto-build/dnsmasq-base-lua \ + DESTDIR=$(CURDIR)/debian/dnsmasq-base-lua \ + PREFIX=$(PREFIX) CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \ + LUA=lua5.4 COPTS="$(COPTS) -DHAVE_LUASCRIPT" + dh_auto_build -p dnsmasq-utils -D contrib/lease-tools + +override_dh_auto_clean: + dh_auto_clean -p dnsmasq-base -- \ + BUILDDIR=debian/auto-build/dnsmasq-base + dh_auto_clean -p dnsmasq-base-lua -- \ + BUILDDIR=debian/auto-build/dnsmasq-base-lua + rm -rf debian/auto-build + dh_auto_clean -p dnsmasq-utils -D contrib/lease-tools + +override_dh_install: + dh_install -p dnsmasq-utils --sourcedir=contrib/lease-tools + dh_install --remaining-packages + +# If 'nodoc' is absent from DEB_BUILD_OPTIONS, Correct name or location of +# some doc files. +# We would prefer do this via dh-exec if it would support dh_installdocs. ifeq (,$(findstring nodoc,$(DEB_BUILD_OPTIONS))) - install -m 755 -d debian/trees/utils/usr/share/man/man1 +execute_after_dh_installdocs: + for d in $(CURDIR)/debian/dnsmasq-base*/usr/share/doc/dnsmasq-base*; do \ + cd $$d; \ + mv readme README.Debian; \ + mv CHANGELOG.archive changelog.archive; \ + mkdir examples; \ + mv dnsmasq.conf.example examples/; \ + done endif - make -C contrib/lease-tools PREFIX=/usr DESTDIR=`pwd`/debian/trees/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(DEB_COPTS)" CC=$(CC) PKG_CONFIG=$(PKG_CONFIG) LIBS="$(LIBS)" - install -m 755 contrib/lease-tools/dhcp_release debian/trees/utils/usr/bin/dhcp_release - install -m 755 contrib/lease-tools/dhcp_release6 debian/trees/utils/usr/bin/dhcp_release6 - install -m 755 contrib/lease-tools/dhcp_lease_time debian/trees/utils/usr/bin/dhcp_lease_time -ifeq (,$(findstring nodoc,$(DEB_BUILD_OPTIONS))) - install -m 644 contrib/lease-tools/dhcp_release.1 debian/trees/utils/usr/share/man/man1/dhcp_release.1 - gzip -9n debian/trees/utils/usr/share/man/man1/dhcp_release.1 - install -m 644 contrib/lease-tools/dhcp_release6.1 debian/trees/utils/usr/share/man/man1/dhcp_release6.1 - gzip -9n debian/trees/utils/usr/share/man/man1/dhcp_release6.1 - install -m 644 contrib/lease-tools/dhcp_lease_time.1 debian/trees/utils/usr/share/man/man1/dhcp_lease_time.1 - gzip -9n debian/trees/utils/usr/share/man/man1/dhcp_lease_time.1 -endif - install -m 644 debian/copyright debian/trees/utils/usr/share/doc/dnsmasq-utils/copyright - install -m 644 debian/changelog debian/trees/utils/usr/share/doc/dnsmasq-utils/changelog.Debian - gzip -9n debian/trees/utils/usr/share/doc/dnsmasq-utils/changelog.Debian -ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) - $(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/trees/utils/usr/bin/dhcp_release - $(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/trees/utils/usr/bin/dhcp_release6 - $(DEB_HOST_GNU_TYPE)-strip -R .note -R .comment debian/trees/utils/usr/bin/dhcp_lease_time -endif - cd debian/trees/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | LC_ALL=C sort -z | xargs -r0 md5sum > DEBIAN/md5sums - dpkg-shlibdeps -Tdebian/utils-substvars debian/trees/utils/usr/bin/dhcp_release debian/trees/utils/usr/bin/dhcp_release6 debian/trees/utils/usr/bin/dhcp_lease_time - dpkg-gencontrol $(PACKAGE_VERSION) -Tdebian/utils-substvars -pdnsmasq-utils -Pdebian/trees/utils - find debian/trees/utils -depth -newermt '$(BUILD_DATE)' -print0 | xargs -0r touch --no-dereference --date='$(BUILD_DATE)' - chown -R root.root debian/trees/utils - chmod -R g-ws debian/trees/utils - dpkg --build debian/trees/utils .. -endif - -define checkdir - test -f Makefile -a -f debian/rules -endef - -# Below here is fairly generic really - -binary: binary-arch binary-indep - -build: -build-arch: -build-indep: - -checkroot: - test root = "`whoami`" - -.PHONY: binary binary-arch binary-indep clean checkroot - +# If 'nodoc' is present in DEB_BUILD_OPTIONS, drop the man pages already +# installed by the upstream build script. Then, let dh_installman do what +# else needs doing. +override_dh_installman: +ifneq (,$(findstring nodoc,$(DEB_BUILD_OPTIONS))) + rm -rf debian/dnsmasq-base*/usr/share/man +endif + dh_installman -p dnsmasq-utils --sourcedir=contrib/lease-tools + dh_installman --remaining-packages diff -Nru dnsmasq-2.89/debian/source/format dnsmasq-2.90/debian/source/format --- dnsmasq-2.89/debian/source/format 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/source/format 2024-05-17 15:33:51.000000000 +0000 @@ -1 +1 @@ -1.0 +3.0 (quilt) diff -Nru dnsmasq-2.89/debian/systemd-helper dnsmasq-2.90/debian/systemd-helper --- dnsmasq-2.89/debian/systemd-helper 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/systemd-helper 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,34 @@ +#!/bin/sh + +. /usr/share/dnsmasq/init-system-common + +case "$1" in + checkconfig) + checkconfig + ;; + start-resolvconf) + start_resolvconf + ;; + stop-resolvconf) + stop_resolvconf + ;; + exec) + # /run may be volatile, so we need to ensure that + # /run/dnsmasq exists here as well as in postinst + if [ ! -d /run/dnsmasq ]; then + mkdir /run/dnsmasq || { [ -d /run/dnsmasq ] || exit 2 ; } + chown dnsmasq:nogroup /run/dnsmasq || exit 2 + fi + exec ${DAEMON} -x /run/dnsmasq/${NAME}${INSTANCE:+.${INSTANCE}}.pid \ + ${MAILHOSTNAME:+ -m ${MAILHOSTNAME}} \ + ${MAILTARGET:+ -t ${MAILTARGET}} \ + ${DNSMASQ_USER:+ -u ${DNSMASQ_USER}} \ + ${DNSMASQ_INTERFACES:+ ${DNSMASQ_INTERFACES}} \ + ${DHCP_LEASE:+ -l ${DHCP_LEASE}} \ + ${DOMAIN_SUFFIX:+ -s ${DOMAIN_SUFFIX}} \ + ${RESOLV_CONF:+ -r ${RESOLV_CONF}} \ + ${CACHESIZE:+ -c ${CACHESIZE}} \ + ${CONFIG_DIR:+ -7 ${CONFIG_DIR}} \ + ${DNSMASQ_OPTS:+ ${DNSMASQ_OPTS}} + ;; +esac diff -Nru dnsmasq-2.89/debian/systemd.service dnsmasq-2.90/debian/systemd.service --- dnsmasq-2.89/debian/systemd.service 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/systemd.service 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -[Unit] -Description=dnsmasq - A lightweight DHCP and caching DNS server -Requires=network.target -Wants=nss-lookup.target -Before=nss-lookup.target -After=network.target - -[Service] -Type=forking -PIDFile=/run/dnsmasq/dnsmasq.pid - -# Test the config file and refuse starting if it is not valid. -ExecStartPre=/etc/init.d/dnsmasq checkconfig - -# We run dnsmasq via the /etc/init.d/dnsmasq script which acts as a -# wrapper picking up extra configuration files and then execs dnsmasq -# itself, when called with the "systemd-exec" function. -ExecStart=/etc/init.d/dnsmasq systemd-exec - -# The systemd-*-resolvconf functions configure (and deconfigure) -# resolvconf to work with the dnsmasq DNS server. They're called like -# this to get correct error handling (ie don't start-resolvconf if the -# dnsmasq daemon fails to start). -ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf -ExecStop=/etc/init.d/dnsmasq systemd-stop-resolvconf - - -ExecReload=/bin/kill -HUP $MAINPID - -[Install] -WantedBy=multi-user.target diff -Nru dnsmasq-2.89/debian/systemd@.service dnsmasq-2.90/debian/systemd@.service --- dnsmasq-2.89/debian/systemd@.service 2024-12-31 11:49:30.000000000 +0000 +++ dnsmasq-2.90/debian/systemd@.service 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -[Unit] -Description=dnsmasq (%i) - A lightweight DHCP and caching DNS server -Requires=network.target -Wants=nss-lookup.target -Before=nss-lookup.target -After=network.target - -[Service] -Type=forking -PIDFile=/run/dnsmasq/dnsmasq.%i.pid - -# Test the config file and refuse starting if it is not valid. -ExecStartPre=/etc/init.d/dnsmasq checkconfig "%i" - -# We run dnsmasq via the /etc/init.d/dnsmasq script which acts as a -# wrapper picking up extra configuration files and then execs dnsmasq -# itself, when called with the "systemd-exec" function. -ExecStart=/etc/init.d/dnsmasq systemd-exec "%i" - -# The systemd-*-resolvconf functions configure (and deconfigure) -# resolvconf to work with the dnsmasq DNS server. They're called like -# this to get correct error handling (ie don't start-resolvconf if the -# dnsmasq daemon fails to start). -ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf "%i" -ExecStop=/etc/init.d/dnsmasq systemd-stop-resolvconf "%i" - - -ExecReload=/bin/kill -HUP $MAINPID - -[Install] -WantedBy=multi-user.target diff -Nru dnsmasq-2.89/debian/tests/compile-time-options dnsmasq-2.90/debian/tests/compile-time-options --- dnsmasq-2.89/debian/tests/compile-time-options 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/compile-time-options 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +. debian/tests/functions + +check_compile_time_options diff -Nru dnsmasq-2.89/debian/tests/compile-time-options+lua dnsmasq-2.90/debian/tests/compile-time-options+lua --- dnsmasq-2.89/debian/tests/compile-time-options+lua 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/compile-time-options+lua 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +. debian/tests/functions + +check_compile_time_options +lua diff -Nru dnsmasq-2.89/debian/tests/control dnsmasq-2.90/debian/tests/control --- dnsmasq-2.89/debian/tests/control 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/control 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,39 @@ +Tests: compile-time-options +Depends: dnsmasq, + dnsmasq-base, +Restrictions: needs-root, + isolation-container, + +Tests: compile-time-options+lua +Depends: dnsmasq, + dnsmasq-base-lua, +Restrictions: needs-root, + isolation-container, + +Tests: get-address+query-dns+check-utils +Depends: bind9, + bind9-dnsutils, + dnsmasq, + dnsmasq-base, + dnsmasq-utils, +Restrictions: needs-root, + allow-stderr, + isolation-container, + +Tests: get-address+query-dns+lua+alt +Depends: bind9, + bind9-dnsutils, + dnsmasq, + dnsmasq-base-lua, +Restrictions: needs-root, + allow-stderr, + isolation-container, + +Tests: get-address+query-dns+sysv+alt +Depends: bind9, + bind9-dnsutils, + dnsmasq, + dnsmasq-base, +Restrictions: needs-root, + allow-stderr, + isolation-container, diff -Nru dnsmasq-2.89/debian/tests/functions dnsmasq-2.90/debian/tests/functions --- dnsmasq-2.89/debian/tests/functions 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,151 @@ +# -*- shell-script -*- + +FUNCTIONS_DIR="debian/tests/functions.d" + +match_or_exit () { + file_to_match="$1" + pattern_file="$2" + + while read line_to_match <&3 && read pattern_line <&4 ; do + if [ "${line_to_match##$pattern_line}" ]; then + echo '!!! MISMATCH !!!' >&2 + echo "Line: ${line_to_match}" >&2 + echo "Pattern: ${pattern_line}" >&2 + exit 1 + fi; + done 3<"${file_to_match}" 4<"${pattern_file}" +} + +linecount () { + wc -l $1 | cut -d' ' -f1 +} + +error_exit () { + echo "ERROR: $1" + exit 1 +} + +stop_dnsmasq_bind_networking () { + systemctl stop dnsmasq.service + systemctl stop named.service + systemctl stop networking.service +} + +configure_and_start_networking () { + #Add interfaces needed for the test + cat ${FUNCTIONS_DIR}/add-to.interfaces >> /etc/network/interfaces + systemctl start networking.service +} + +configure_and_start_bind () { + cp ${FUNCTIONS_DIR}/db.autopkg.test /etc/bind/ + cat ${FUNCTIONS_DIR}/add-to.named.conf.local >> /etc/bind/named.conf.local + cp ${FUNCTIONS_DIR}/named.conf.options /etc/bind/named.conf.options + systemctl start named.service +} + +configure_and_start_dnsmasq () { + alt_mode=0 + lua_mode=0 + sysv_mode=0 + service='dnsmasq.service' + sysv_param2='' + conf_dir='/etc/dnsmasq.d' + + while [ -n "$1" ]; do + case "$1" in + alt|lua|sysv) eval ${1}_mode=1 ;; + *) error_exit "configure_and_start_dnsmasq(): invalid flag '$1'" + esac + shift + done + + if [ ${alt_mode} -eq 1 ]; then + cp ${FUNCTIONS_DIR}/dnsmasq.alt-autopkgtest.default /etc/default/dnsmasq.alt + cp /etc/dnsmasq.conf /etc/dnsmasq.alt.conf + mkdir /etc/dnsmasq.alt.d + service='dnsmasq@alt.service' + sysv_param2='alt' + conf_dir='/etc/dnsmasq.alt.d' + fi + + cp ${FUNCTIONS_DIR}/dnsmasq-autopkgtest.conf "${conf_dir}" + + if [ ${lua_mode} -eq 1 ]; then + mkdir -p /usr/local/share/dnsmasq + cp ${FUNCTIONS_DIR}/log.lua /usr/local/share/dnsmasq/ + echo "dhcp-luascript=/usr/local/share/dnsmasq/log.lua\n" \ + >>"${conf_dir}"/dnsmasq-autopkgtest.conf + fi + + if [ ${sysv_mode} -eq 1 ]; then + SYSTEMCTL_SKIP_REDIRECT=1 /etc/init.d/dnsmasq start "${sysv_param2}" + else + systemctl enable "${service}" + systemctl start "${service}" + fi +} + +check_compile_time_options () { + journalctl -b -u dnsmasq + echo ~~~ Check compile time options... + journalctl -b -u dnsmasq -g '[a-z]+: ' --output cat >options.msg + cat options.msg + match_or_exit options.msg ${FUNCTIONS_DIR}/options${1}.patterns +} + +get_address_on_veth1_and_check_the_result () { + echo ~~~ Get an address on veth1 and check the result... + ip netns exec clientnet ifup veth1 + ip netns exec clientnet ip addr show dev veth1 >ip-addr.out 2>&1 + cat ip-addr.out + match_or_exit ip-addr.out ${FUNCTIONS_DIR}/ip-addr.patterns +} + +query_test_zone_records_and_check_the_result () { + echo ~~~ Query some test zone records and check the result... + ip netns exec clientnet dig +short SOA autopkg.test >dig.out 2>&1 + ip netns exec clientnet dig +short NS autopkg.test >>dig.out 2>&1 + ip netns exec clientnet dig +short A ns.autopkg.test >>dig.out 2>&1 + ip netns exec clientnet dig +short A dhcp3.autopkg.test >>dig.out 2>&1 + cat dig.out + if [ `linecount dig.out` -ne `linecount ${FUNCTIONS_DIR}/dig.patterns` ] ; then + error_exit 'empty or unexpected output' + fi + match_or_exit dig.out ${FUNCTIONS_DIR}/dig.patterns +} + +check_utils () { + #Test dhcp_lease_time and dhcp_release + leases_file='/var/lib/misc/dnsmasq.leases' + client_ip_address=`cut -d' ' -f3 $leases_file` + client_mac_address=`cut -d' ' -f2 $leases_file` + echo ~~~ Test dhcp_lease_time... + if ! dhcp_lease_time $client_ip_address; then + error_exit "'dhcp_lease_time $client_ip_address' failed with return code $?" + else + #Add \n to dhcp_lease_time's output + echo '' + fi + echo ~~~ Test dhcp_release... + cat $leases_file + if ! dhcp_release veth0 $client_ip_address 1-$client_mac_address; then + error_exit "'dhcp_release veth0 $client_ip_address 1-$client_mac_address' failed with return code $?0" + fi + if [ -n "`cat $leases_file`" ]; then + cat $leases_file + error_exit "$leases_file is not empty" + fi +} + +check_lua_log () { + log_file='/var/log/dnsmasq-lua.log' + echo ~~~ Check log file generated by lua script + ls -l ${log_file} + if [ -s ${log_file} ]; then + cat ${log_file} + match_or_exit ${log_file} ${FUNCTIONS_DIR}/log.patterns + else + error_exit "${log_file} is empty" + fi +} diff -Nru dnsmasq-2.89/debian/tests/functions.d/add-to.interfaces dnsmasq-2.90/debian/tests/functions.d/add-to.interfaces --- dnsmasq-2.89/debian/tests/functions.d/add-to.interfaces 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/add-to.interfaces 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,18 @@ + +auto dummy0 +iface dummy0 inet static + pre-up ip link add dummy0 type dummy + address 192.168.141.1 + netmask 255.255.255.248 + post-down ip link del dummy0 + +auto veth0 +iface veth0 inet static + pre-up ip netns add clientnet + pre-up ip link add veth0 type veth peer veth1 netns clientnet + address 192.168.142.1 + netmask 255.255.255.248 + post-down ip link del veth0 + post-down ip netns del clientnet + +iface veth1 inet dhcp diff -Nru dnsmasq-2.89/debian/tests/functions.d/add-to.named.conf.local dnsmasq-2.90/debian/tests/functions.d/add-to.named.conf.local --- dnsmasq-2.89/debian/tests/functions.d/add-to.named.conf.local 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/add-to.named.conf.local 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,2 @@ +zone "autopkg.test" { type master; file "/etc/bind/db.autopkg.test"; }; + diff -Nru dnsmasq-2.89/debian/tests/functions.d/db.autopkg.test dnsmasq-2.90/debian/tests/functions.d/db.autopkg.test --- dnsmasq-2.89/debian/tests/functions.d/db.autopkg.test 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/db.autopkg.test 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,18 @@ +$TTL 604800 +@ IN SOA ns.autopkg.test. hostmaster.autopkg.test. ( + 2 ; Serial + 604800 ; Refresh + 86400 ; Retry + 2419200 ; Expire + 300 ) ; Negative Cache TTL +; +@ IN NS ns +ns IN A 192.168.141.1 +host IN A 192.168.142.1 +dhcp0 IN A 192.168.142.2 +dhcp1 IN A 192.168.142.3 +dhcp2 IN A 192.168.142.4 +dhcp3 IN A 192.168.142.5 +dhcp4 IN A 192.168.142.6 +brdcst IN A 192.168.142.7 + diff -Nru dnsmasq-2.89/debian/tests/functions.d/dig.patterns dnsmasq-2.90/debian/tests/functions.d/dig.patterns --- dnsmasq-2.89/debian/tests/functions.d/dig.patterns 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/dig.patterns 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,4 @@ +ns.autopkg.test. hostmaster.autopkg.test. 2 604800 86400 2419200 300 +ns.autopkg.test. +192.168.141.1 +192.168.142.5 diff -Nru dnsmasq-2.89/debian/tests/functions.d/dnsmasq-autopkgtest.conf dnsmasq-2.90/debian/tests/functions.d/dnsmasq-autopkgtest.conf --- dnsmasq-2.89/debian/tests/functions.d/dnsmasq-autopkgtest.conf 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/dnsmasq-autopkgtest.conf 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,6 @@ +no-resolv +server=/autopkg.test/192.168.141.1 +listen-address=192.168.142.1,127.0.0.1 +bind-interfaces +dhcp-range=192.168.142.2,192.168.142.6 +dhcp-authoritative diff -Nru dnsmasq-2.89/debian/tests/functions.d/dnsmasq.alt-autopkgtest.default dnsmasq-2.90/debian/tests/functions.d/dnsmasq.alt-autopkgtest.default --- dnsmasq-2.89/debian/tests/functions.d/dnsmasq.alt-autopkgtest.default 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/dnsmasq.alt-autopkgtest.default 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,42 @@ +# This file has six functions: +# 1) to completely disable starting this dnsmasq instance +# 2) to set DOMAIN_SUFFIX by running `dnsdomainname` +# 3) to select an alternative config file +# by setting DNSMASQ_OPTS to --conf-file= +# 4) to tell dnsmasq to read the files in /etc/dnsmasq.d for +# more configuration variables. +# 5) to stop the resolvconf package from controlling dnsmasq's +# idea of which upstream nameservers to use. +# 6) to avoid using this dnsmasq instance as the system's default resolver +# by setting DNSMASQ_EXCEPT="lo" +# For upgraders from very old versions, all the shell variables set +# here in previous versions are still honored by the init script +# so if you just keep your old version of this file nothing will break. + +#DOMAIN_SUFFIX=`dnsdomainname` +DNSMASQ_OPTS="--conf-file=/etc/dnsmasq.alt.conf" + +# The dnsmasq daemon is run by default conforming to the Debian Policy. +# To disable the service, +# for SYSV init, use "update-rc.d dnsmasq disable", +# for systemd, use "systemctl disable dnsmasq". + +# By default search this drop directory for configuration options. +# Libvirt leaves a file here to make the system dnsmasq play nice. +# Comment out this line if you don't want this. The dpkg-* are file +# endings which cause dnsmasq to skip that file. This avoids pulling +# in backups made by dpkg. +CONFIG_DIR=/etc/dnsmasq.alt.d,.dpkg-dist,.dpkg-old,.dpkg-new + +# If the resolvconf package is installed, dnsmasq will use its output +# rather than the contents of /etc/resolv.conf to find upstream +# nameservers. Uncommenting this line inhibits this behaviour. +# Note that including a "resolv-file=" line in +# /etc/dnsmasq.conf is not enough to override resolvconf if it is +# installed: the line below must be uncommented. +#IGNORE_RESOLVCONF=yes + +# If the resolvconf package is installed, dnsmasq will tell resolvconf +# to use dnsmasq under 127.0.0.1 as the system's default resolver. +# Uncommenting this line inhibits this behaviour. +#DNSMASQ_EXCEPT="lo" diff -Nru dnsmasq-2.89/debian/tests/functions.d/ip-addr.patterns dnsmasq-2.90/debian/tests/functions.d/ip-addr.patterns --- dnsmasq-2.89/debian/tests/functions.d/ip-addr.patterns 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/ip-addr.patterns 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,6 @@ +*: veth1@if*: mtu 1500 qdisc noqueue state UP group default qlen 1000 + link/ether ??:??:??:??:??:?? brd ff:ff:ff:ff:ff:ff link-netnsid 0 + inet 192.168.142.?/29 brd 192.168.142.7 scope global dynamic veth1 + valid_lft 3[56][0-9][0-9]sec preferred_lft 3[56][0-9][0-9]sec + inet6 fe80::*:*:*:*/64 scope link* + valid_lft forever preferred_lft forever diff -Nru dnsmasq-2.89/debian/tests/functions.d/log.lua dnsmasq-2.90/debian/tests/functions.d/log.lua --- dnsmasq-2.89/debian/tests/functions.d/log.lua 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/log.lua 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,40 @@ +-- Lua script logging calls from dnsmasq + +-- Open the log file in append mode +logfile = assert(io.open("/var/log/dnsmasq-lua.log", "a")) + +-- Prepend date and time to a string and write the result to the log file +function __log(str) + logfile:write(os.date("!%FT%TZ ")..str.."\n") +end + +-- flush the log file +function __flush_log() + logfile:flush() +end + +-- Log a call to init() +function init() + __log("initialising") + __flush_log() +end + +-- Log a call to shutdown() +function shutdown() + __log("shutting down") + __flush_log() +end + +-- Log a call to lease() including all arguments +function lease(operation, params) + local lines = {} + __log(operation.." lease") + for key,value in pairs(params) do + table.insert(lines, key..": "..value) + end + table.sort(lines) + for index,line in ipairs(lines) do + __log("\t"..line) + end + __flush_log() +end diff -Nru dnsmasq-2.89/debian/tests/functions.d/log.patterns dnsmasq-2.90/debian/tests/functions.d/log.patterns --- dnsmasq-2.89/debian/tests/functions.d/log.patterns 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/log.patterns 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,10 @@ +????-??-??T??:??:??Z initialising +????-??-??T??:??:??Z add lease +????-??-??T??:??:??Z client_id: ??:??:??:??:??:??:??:??:??:??:??:??:??:??:??:??:??:??:?? +????-??-??T??:??:??Z data_missing: 1.0 +????-??-??T??:??:??Z hostname: ?* +????-??-??T??:??:??Z interface: veth0 +????-??-??T??:??:??Z ip_address: 192.168.142.[2-6] +????-??-??T??:??:??Z lease_expires: [1-9]* +????-??-??T??:??:??Z mac_address: ??:??:??:??:??:?? +????-??-??T??:??:??Z time_remaining: 3600.0 diff -Nru dnsmasq-2.89/debian/tests/functions.d/named.conf.options dnsmasq-2.90/debian/tests/functions.d/named.conf.options --- dnsmasq-2.89/debian/tests/functions.d/named.conf.options 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/named.conf.options 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,6 @@ +options { + directory "/var/cache/bind"; + listen-on { 192.168.141.1; }; + recursion no; +}; + diff -Nru dnsmasq-2.89/debian/tests/functions.d/options+lua.patterns dnsmasq-2.90/debian/tests/functions.d/options+lua.patterns --- dnsmasq-2.89/debian/tests/functions.d/options+lua.patterns 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/options+lua.patterns 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1 @@ +*: IPv6 GNU-getopt DBus no-UBus i18n IDN2 DHCP DHCPv6 Lua TFTP conntrack ipset nftset auth cryptohash DNSSEC loop-detect inotify dumpfile diff -Nru dnsmasq-2.89/debian/tests/functions.d/options.patterns dnsmasq-2.90/debian/tests/functions.d/options.patterns --- dnsmasq-2.89/debian/tests/functions.d/options.patterns 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/functions.d/options.patterns 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1 @@ +*: IPv6 GNU-getopt DBus no-UBus i18n IDN2 DHCP DHCPv6 no-Lua TFTP conntrack ipset nftset auth cryptohash DNSSEC loop-detect inotify dumpfile diff -Nru dnsmasq-2.89/debian/tests/get-address+query-dns+check-utils dnsmasq-2.90/debian/tests/get-address+query-dns+check-utils --- dnsmasq-2.89/debian/tests/get-address+query-dns+check-utils 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/get-address+query-dns+check-utils 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +. debian/tests/functions + +stop_dnsmasq_bind_networking +configure_and_start_networking +configure_and_start_bind +configure_and_start_dnsmasq + +get_address_on_veth1_and_check_the_result + +query_test_zone_records_and_check_the_result + +check_utils + +#Done +echo Looks good. diff -Nru dnsmasq-2.89/debian/tests/get-address+query-dns+lua+alt dnsmasq-2.90/debian/tests/get-address+query-dns+lua+alt --- dnsmasq-2.89/debian/tests/get-address+query-dns+lua+alt 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/get-address+query-dns+lua+alt 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +. debian/tests/functions + +stop_dnsmasq_bind_networking +configure_and_start_networking +configure_and_start_bind +configure_and_start_dnsmasq lua alt + +get_address_on_veth1_and_check_the_result + +query_test_zone_records_and_check_the_result + +check_lua_log + +#Done +echo Looks good. diff -Nru dnsmasq-2.89/debian/tests/get-address+query-dns+sysv+alt dnsmasq-2.90/debian/tests/get-address+query-dns+sysv+alt --- dnsmasq-2.89/debian/tests/get-address+query-dns+sysv+alt 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/tests/get-address+query-dns+sysv+alt 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,18 @@ +#!/bin/sh + +set -e + +. debian/tests/functions + +stop_dnsmasq_bind_networking +configure_and_start_networking +configure_and_start_bind +configure_and_start_dnsmasq sysv alt + +get_address_on_veth1_and_check_the_result + +query_test_zone_records_and_check_the_result + +#Done +echo Looks good. +SYSTEMCTL_SKIP_REDIRECT=1 /etc/init.d/dnsmasq stop alt diff -Nru dnsmasq-2.89/debian/upstream/metadata dnsmasq-2.90/debian/upstream/metadata --- dnsmasq-2.89/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/upstream/metadata 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,9 @@ +Cite-As: dnsmasq +Contact: simon@thekelleys.org.uk +Security-Contact: https://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss +Repository: https://thekelleys.org.uk/git/dnsmasq.git +Repository-Browse: https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=summary +Changelog: https://thekelleys.org.uk/dnsmasq/CHANGELOG +Documentation: https://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html +FAQ: https://thekelleys.org.uk/dnsmasq/docs/FAQ +Bug-Submit: https://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss diff -Nru dnsmasq-2.89/debian/upstream/signing-key.asc dnsmasq-2.90/debian/upstream/signing-key.asc --- dnsmasq-2.89/debian/upstream/signing-key.asc 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/upstream/signing-key.asc 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,63 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFMbjUMBEACsU1Xk8+uu/EsGVJTh9Tn31C2e0ycd0voBVT7cTdtXpzeiNR+o +/zUAi95ds7FiecpZJp1nRO4vNzvaaAPZhFsFVLzZYyIVABgTXsskT88xbZvzb4W5 +KKRWVhoTQxVDgj1+dXLUXULTB6rg02WEhqnix/qf/zFdM9I4/3pRHJn9k+3XKygR +on+nYtljfn3AKBelCo1y28istC6wCncoH11b/qdQtlfxVXaJY4HF27V0MqFFmDMg +cuhOHR7DnhymeDh7GmLfTHJ4LUFG+TecqCjiYhyWcuv2wuSb0EPXUKHJQVViQ8qg +KyPm1ly6uFP0CYdVavO7/oJwKFBIChECrj7BQ4GsImMHeuSzfWno7qy6Fxoxx2+g +0F9cdXWvcxFDGPQsL5vXp8KYNwBrzmijRzQ2ZAnrbG+ilFCkJCbxXcrhzpd4tKwE +0dgcyPL1Ma/lrznhL4ZuOzjVMgLNne7WiPpBNRqI1GoT0pUn6as4pU3En8B+K7zy +MLVfHvI1+iH45fP5bZwYSbXCa85v4+xqljYrzs9giaROEsXe/tsXvuc6JPCcmJXk +CUO3c3QVxqDFt9OYuTHIR8hqehDPLgFgzKqVuoAwMkhTf/zZNGlsy4jvKXQNcZ50 +uD4mWO3e+gykNW/OH+88IoCR0rgjQ6trMLOceZFnrtvxwRL//lMndGCTYQARAQAB +tB1TaW1vbiBLZWxsZXkgPHNya0BkZWJpYW4ub3JnPokCNwQTAQgAIQUCUyDDdAIb +AwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRAVzdpq4ZE1oqFGD/9LkbZFigc1 +jbZ5zIbmGkGvfniWp1mJhEcpgKNfb2MMiu1lKULccIvfVyIY5WDrrpoPnHLnhYA9 +OXHcwVADGBayoVOQgIePrMV0V24uYjUh9+9zGRwQrCLo0rl/l07GKH0S1dxDUeyh +JRYZGYEqW2+3XDJqIbfsDzSmPNCyjVvqSvkkt0YyuNbH0+cVEoJ1Q2HmfEhvgd4L +lHZDyhMVqKlKmlnCa8DmhwK+EyzJgLKITqjxBO3NOqPmYZlp8irLXyHAH1sDafaB +wRjV9cNX2TLTwn3wDdUmoAwMz1jopi/61A0kEglENYaa+NH/UnqfWOo7riXuZNwG +VP/F/KlMV+JdXMY34fcSIQMWk9cpxzhpuOJjwhoK7g/yq8q9578QXv4VR6ndH+Le +HDRrm2Ftnih/Ut8unqqDteMJnd3YxSK3Ep78WgVBL9y2Qo3CyKY6VSXlshWZokwy +rwVS8uLqIGAUzLwsKTYi1nmsDb7mQZqUbPBxYN2mrroD7Pr1/XAV8oNxw6l84nzf +zObEKvNZLFtWctNpFJXhWhtm/AeQBdkYKcMyTrwQt9Q0XMYKUGE05U+oAdtTvgCR +JLltqzmt5yMpTPncNmXVoA5YvEVdCU6/Gxpn3Aea8ckBmIqxxQY1QFdEr2nvxPNA +SbkvHDNDr9XUlKQDqjherurKBIBEiKCMnLQmU2ltb24gS2VsbGV5IDxzaW1vbkB0 +aGVrZWxsZXlzLm9yZy51az6JAjoEEwEIACQCGwMFCwkIBwMFFQoJCAsFFgIDAQAC +HgECF4AFAlMgw44CGQEACgkQFc3aauGRNaLaZg/+PR41J3P7omGv6XD+TiAXfJQo +R5RfzQoeLNUQEnir/XBulg45203cYHEurchEhSTn2f4WVtFgxJrgId7XGYdf8oIZ +IjBd82fpwdMwhbfcv/6iqzWL0+2vaPmBqE7iwDTatI888q5TyXppGe8L5/VjX0aB +vmVIPyEE9BFQas+vv5byUkU542FxPApGsv0W0P1pKabLl0F7ItPFPuaD0+K1kwBr +WbuGhBKMV9jGHB4qdX/21FBczgAf3J9yJ22vm6orCwwhptxde+DSn7vqZNjDtHGr +kUWDzKAQBy1g4BmTl6IoVgYKZXAVBGMtYUjS+80VV+QE9meVqmtX1aJJEnf0/BRd +v9CeD46hZArwXwi/AWFs300pEfzwcC+9T5xc3jlSdYdWxeQDV7XwK2VCOhxjFqTm ++ehP2Gh14Wfpc34jN9jMJ3OowxzN5iZxGYzkHLFhM+0IKEeWEjxRWOoJgV5PmNvG +7IBbzt8O9xo550h7JmXZVsfSpkFpzJPy0Puz1JeyH/niCeDwKkhEHXQTk/4O+EOD +RxruJbwIYGeO2lNfPn2Hcb1aHvSclx7GGOYDzI4jN0UcYroJpvHZU+0X2ClpCTAW +5IshgHkOkdUQ1c7S+5zPTeLbW+pxTlbWClA0NYMbSn68//i/DMstyBEwtTWYJLmg +5V3HWzRd/6BwKZfDSuu5Ag0EUyDDoQEQAMfQfa2tw3+OJFGMQEzLJSoXYN8/HnZE +gKNlcMuYzhheQLgu/MfcQJ7mnCIdn6xdPaalfLmYx63tM47/NGEM1+MSEvovPiRG +0OLxzSgwei9DiGeNEgsPTLXSZ5EVSXCM1+e9mT1ExT9aGLNnpCd6kIyWIcKCVMot ++XC70R9prWLeyKSh0FAZ0Pwv9i23osJVGOtJjND+WZ0uCeN29ocfN0b64yF4nPRc +9IbcmYIDgNU3RybK2Z/dupbthTisRjHRI3iX3/tiymXF3J0sSvsCluWIJWmyltS3 +Xyk/wfKVJz6OouiJjTj5utXVnCGptCDw+DCcj89vx1N0+0Dhm1cQcNZvXjMbVDTs +uU+eVpJbxU6y8N+nXpAXjEw4jMi3zNpqKtkyv2YpoqY5HhGLybgrY0zwSQOyMNf9 +lZ5J7znq5gEmiMXnG9OPEw7PPSvm6QfbHPY/jAOgxsu7Fme7k303D5KkyGkkbzQi +YyEtMZvbOMH/uECi2uHGB72qiGpEYjMtHhihaRCBl+0bY8sH83He690qNQHSdStj +aKXcecduE/v5iO0mOYIHdsEHhKlWsE1GXXVLofBr68UBhYV6/AGXko4Pr+dXLzau +N4kALDx6WltFu3qUvoD+uEoLq7IXULMo5Pyd7bO4qGQMKykaXTb5o6dqdu4GzWIU +w1fr9kLEmo29ABEBAAGJAh8EGAEIAAkFAlMgw6ECGwwACgkQFc3aauGRNaIjqA/+ +PXuaM6JHuudLycmB0iKAwyB5csOFGpF3b9FgMR68TC4jzi5J5hJZASl0cO/e0ytQ +srDUBbH74y+WaA4ldwBVYr0j/2hqzIjrnGMtgWeHFPLV3sKw8DGuNx1/cOoljJXz +i1WWSHIwDvaj3uZ9CwHt+4/abR7kdvMcnFhQVA4zuzZWFqpp+CDkkJNVwB9zxtAQ +wGTGF4cQ0IvTkhCo6DQhZZVTeyn+nBKxzzWijniWc0LyRsum03MxZ6E7UVIInCTj +dXTalnO8wColwIx5FV4nTMxdsKKgnIXmLexBdd03bW9TkowWf2C2XfDN+pDS8X3M +zO6zAyogqJhAiBFjnRzkOw0cw1VTL00o8uiWdMeu7OKOKeQbUilMAn4MweKB57mc +582kjeGmwdZgWFA4BJ2eiH7HwjxiynwMdZwQEBdOTNLbggHk3/mScF8U1KcJhjAF +f7Ne+Z0feG/8GgKl5aj3ucl821+dfpzB79lLo+kmd1qkDyDiUR5yN6P8l8k6IAUJ +z2KUe0BjtO6VFFw0xni05dkrXdfo7IO79ictHmEn+g3QO8ZLUGRwdtZ1cMhTkm7F +hH8Bdby0y4SoqluvHbri++cC91i1I3a92kHi/8O45rnLhVt+sOfxY1QnSIYh5OFw +GMqMCNDTEL7ESiFaFhSXkmzzVntlyvOBMlgz3IGh2hA= +=otES +-----END PGP PUBLIC KEY BLOCK----- diff -Nru dnsmasq-2.89/debian/watch dnsmasq-2.90/debian/watch --- dnsmasq-2.89/debian/watch 1970-01-01 00:00:00.000000000 +0000 +++ dnsmasq-2.90/debian/watch 2024-05-17 15:33:51.000000000 +0000 @@ -0,0 +1,5 @@ +version=4 +opts=\ +pgpmode=auto \ +https://thekelleys.org.uk/dnsmasq/ \ +dnsmasq-([\d.]+)@ARCHIVE_EXT@ diff -Nru dnsmasq-2.89/dnsmasq.conf.example dnsmasq-2.90/dnsmasq.conf.example --- dnsmasq-2.89/dnsmasq.conf.example 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/dnsmasq.conf.example 2024-02-13 13:49:15.000000000 +0000 @@ -664,7 +664,7 @@ # Provide an alias for a "local" DNS name. Note that this _only_ works # for targets which are names from DHCP or /etc/hosts. Give host # "bert" another name, bertrand -#cname=bertand,bert +#cname=bertrand,bert # For debugging purposes, log each DNS query as it passes through # dnsmasq. diff -Nru dnsmasq-2.89/man/dnsmasq.8 dnsmasq-2.90/man/dnsmasq.8 --- dnsmasq-2.89/man/dnsmasq.8 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/man/dnsmasq.8 2024-02-13 13:49:15.000000000 +0000 @@ -183,7 +183,8 @@ .TP .B \-P, --edns-packet-max= Specify the largest EDNS.0 UDP packet which is supported by the DNS -forwarder. Defaults to 4096, which is the RFC5625-recommended size. +forwarder. Defaults to 1232, which is the recommended size following the +DNS flag day in 2020. Only increase if you know what you are doing. .TP .B \-Q, --query-port= Send outbound DNS queries from, and listen for their replies on, the @@ -269,16 +270,25 @@ it may be qualified with "/4" or "/6" to specify only the IPv4 or IPv6 addresses associated with the interface. Since any defined authoritative zones are also available as part of the normal recusive DNS service supplied by dnsmasq, it can make sense to have an --auth-server declaration with no interfaces or address, but simply specifying the primary external nameserver. .TP -.B --local-service +.B --local-service[=net|host] +Without parameter or with net parameter, restricts service to connected network. Accept DNS queries only from hosts whose address is on a local subnet, -ie a subnet for which an interface exists on the server. This option +ie a subnet for which an interface exists on the server. With host parameter, listens +only on lo interface and accepts queries from localhost only. This option only has effect if there are no \fB--interface\fP, \fB--except-interface\fP, \fB--listen-address\fP or \fB--auth-server\fP options. It is intended to be set as a default on installation, to allow unconfigured installations to be useful but also safe from being used for DNS amplification attacks. -.TP +.TP .B \-2, --no-dhcp-interface= -Do not provide DHCP or TFTP on the specified interface, but do provide DNS service. +Do not provide DHCP, TFTP or router advertisement on the specified interface, but do provide DNS service. +.TP +.B --no-dhcpv4-interface= +Disable only IPv4 DHCP on the specified interface. +.TP +.B +--no-dhcpv6-interface= +Disable IPv6 DHCP and router advertisement on the specified interface. .TP .B \-a, --listen-address= Listen on the given IP address(es). Both @@ -375,6 +385,20 @@ .B --filter-AAAA Remove AAAA records from answers. No IPv6 addresses will be returned. .TP +.B --filter-rr=[,...] +Remove records of the specified type(s) from answers. The otherwise-nonsensical --filter-rr=ANY has +a special meaning: it filters replies to queries for type ANY. Everything other than A, AAAA, MX and CNAME +records are removed. Since ANY queries with forged source addresses can be used in DNS amplification attacks +(replies to ANY queries can be large) this defangs such attacks, whilst still supporting the +one remaining possible use of ANY queries. See RFC 8482 para 4.3 for details. +.TP +.B --cache-rr=[,...] +By default, dnsmasq caches A, AAAA, CNAME and SRV DNS record types. +This option adds other record types to the cache. The RR-type can be given +as a name such as TXT or MX or a decimal number. A single --cache-rr option +can take a comma-separated list of RR-types and more than one --cache-rr option +is allowed. Use --cache-rr=ANY to enable caching for all RR-types. +.TP .B \-r, --resolv-file= Read the IP addresses of the upstream nameservers from , instead of /etc/resolv.conf. For the format of this file see @@ -907,6 +931,15 @@ when using this option, then the cache should be disabled using --cache-size=0. In most cases, enabling DNSSEC validation within dnsmasq is a better option. See --dnssec for details. .TP +.B --dnssec-limits=[,.......] +Override the default resource limits applied to DNSSEC validation. Cryptographic operations are expensive and crafted domains +can DoS a DNSSEC validator by forcing it to do hundreds of thousands of such operations. To avoid this, the dnsmasq validation code +applies limits on how much work will be expended in validation. If any of the limits are exceeded, the validation will fail and the +domain treated as BOGUS. There are four limits, in order(default values in parens): number a signature validation fails per RRset(20), number of signature validations and +hash computations per query(200), number of sub-queries to fetch DS and DNSKEY RRsets per query(40), and the number of iterations in a NSEC3 record(150). +The maximum values reached during validation are stored, and dumped as part of the stats generated by SIGUSR1. Supplying a limit value of 0 leaves the default in place, so +\fB--dnssec-limits=0,0,20\fP sets the number of sub-queries to 20 whilst leaving the other limits at default values. +.TP .B --dnssec-debug Set debugging mode for the DNSSEC validation, set the Checking Disabled bit on upstream queries, and don't convert replies which do not validate to responses with @@ -1297,7 +1330,15 @@ or .B --dhcp-option = option:ntp-server, 192.168.0.4 The special address 0.0.0.0 is taken to mean "the address of the -machine running dnsmasq". +machine running dnsmasq". + +An option without data is valid, and includes just the option without data. +(There is only one option with a zero length data field currently defined for DHCPv4, 80:rapid commit, so this feature is not very useful in practice). Options for which dnsmasq normally +provides default values can be ommitted by defining the option with no data. These are +netmask, broadcast, router, DNS server, domainname and hostname. Thus, for DHCPv4 +.B --dhcp-option = option:router +will result in no router option being sent, rather than the default of the host on which dnsmasq is running. For DHCPv6, the same is true of the options DNS server and refresh time. + Data types allowed are comma separated dotted-quad IPv4 addresses, []-wrapped IPv6 addresses, a decimal number, colon-separated hex digits @@ -2228,6 +2269,10 @@ utility would be dig +short chaos txt cachesize.bind +.TP +.B --max-tcp-connections= +The maximum number of concurrent TCP connections. The application forks to +handle each TCP request. The default maximum is 20. .SH CONFIG FILE At startup, dnsmasq reads @@ -2273,7 +2318,7 @@ of names that have been inserted into the cache. The number of cache hits and misses and the number of authoritative queries answered are also given. For each upstream server it gives the number of queries sent, and the number which -resulted in an error. In +resulted in an error. It also gives information on the number of forks for TCP connections. In .B --no-daemon mode or when full logging is enabled (\fB--log-queries\fP), a complete dump of the contents of the cache is made. diff -Nru dnsmasq-2.89/src/arp.c dnsmasq-2.90/src/arp.c --- dnsmasq-2.89/src/arp.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/arp.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/auth.c dnsmasq-2.90/src/auth.c --- dnsmasq-2.89/src/auth.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/auth.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/blockdata.c dnsmasq-2.90/src/blockdata.c --- dnsmasq-2.89/src/blockdata.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/blockdata.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ static struct blockdata *keyblock_free; static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced; -static void blockdata_expand(int n) +static void add_blocks(int n) { struct blockdata *new = whine_malloc(n * sizeof(struct blockdata)); @@ -47,7 +47,7 @@ /* Note that daemon->cachesize is enforced to have non-zero size if OPT_DNSSEC_VALID is set */ if (option_bool(OPT_DNSSEC_VALID)) - blockdata_expand(daemon->cachesize); + add_blocks(daemon->cachesize); } void blockdata_report(void) @@ -58,50 +58,61 @@ blockdata_alloced * sizeof(struct blockdata)); } +static struct blockdata *new_block(void) +{ + struct blockdata *block; + + if (!keyblock_free) + add_blocks(50); + + if (keyblock_free) + { + block = keyblock_free; + keyblock_free = block->next; + blockdata_count++; + if (blockdata_hwm < blockdata_count) + blockdata_hwm = blockdata_count; + block->next = NULL; + return block; + } + + return NULL; +} + static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len) { struct blockdata *block, *ret = NULL; struct blockdata **prev = &ret; size_t blen; - while (len > 0) + do { - if (!keyblock_free) - blockdata_expand(50); - - if (keyblock_free) - { - block = keyblock_free; - keyblock_free = block->next; - blockdata_count++; - } - else + if (!(block = new_block())) { /* failed to alloc, free partial chain */ blockdata_free(ret); return NULL; } - - if (blockdata_hwm < blockdata_count) - blockdata_hwm = blockdata_count; - - blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len; - if (data) - { - memcpy(block->key, data, blen); - data += blen; - } - else if (!read_write(fd, block->key, blen, 1)) + + if ((blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len) > 0) { - /* failed read free partial chain */ - blockdata_free(ret); - return NULL; + if (data) + { + memcpy(block->key, data, blen); + data += blen; + } + else if (!read_write(fd, block->key, blen, 1)) + { + /* failed read free partial chain */ + blockdata_free(ret); + return NULL; + } } + len -= blen; *prev = block; prev = &block->next; - block->next = NULL; - } + } while (len != 0); return ret; } @@ -111,6 +122,58 @@ return blockdata_alloc_real(0, data, len); } +/* Add data to the end of the block. + newlen is length of new data, NOT total new length. + Use blockdata_alloc(NULL, 0) to make empty block to add to. */ +int blockdata_expand(struct blockdata *block, size_t oldlen, char *data, size_t newlen) +{ + struct blockdata *b; + + /* find size of current final block */ + for (b = block; oldlen > KEYBLOCK_LEN && b; b = b->next, oldlen -= KEYBLOCK_LEN); + + /* chain to short for length, something is broken */ + if (oldlen > KEYBLOCK_LEN) + { + blockdata_free(block); + return 0; + } + + while (1) + { + struct blockdata *new; + size_t blocksize = KEYBLOCK_LEN - oldlen; + size_t size = (newlen <= blocksize) ? newlen : blocksize; + + if (size != 0) + { + memcpy(&b->key[oldlen], data, size); + data += size; + newlen -= size; + } + + /* full blocks from now on. */ + oldlen = 0; + + if (newlen == 0) + break; + + if ((new = new_block())) + { + b->next = new; + b = new; + } + else + { + /* failed to alloc, free partial chain */ + blockdata_free(block); + return 0; + } + } + + return 1; +} + void blockdata_free(struct blockdata *blocks) { struct blockdata *tmp; diff -Nru dnsmasq-2.89/src/bpf.c dnsmasq-2.90/src/bpf.c --- dnsmasq-2.89/src/bpf.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/bpf.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/cache.c dnsmasq-2.90/src/cache.c --- dnsmasq-2.89/src/cache.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/cache.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,6 +29,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class, time_t now, unsigned long ttl, unsigned int flags); static void dump_cache_entry(struct crec *cache, time_t now); +static char *querystr(char *desc, unsigned short type); /* type->string mapping: this is also used by the name-hash function as a mixing table. */ /* taken from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml */ @@ -123,6 +124,7 @@ { 258, "AVC" }, /* Application Visibility and Control [Wolfgang_Riedel] AVC/avc-completed-template 2016-02-26*/ { 259, "DOA" }, /* Digital Object Architecture [draft-durand-doa-over-dns] DOA/doa-completed-template 2017-08-30*/ { 260, "AMTRELAY" }, /* Automatic Multicast Tunneling Relay [RFC8777] AMTRELAY/amtrelay-completed-template 2019-02-06*/ + { 261, "RESINFO" }, /* Resolver Information as Key/Value Pairs https://datatracker.ietf.org/doc/draft-ietf-add-resolver-info/06/ */ { 32768, "TA" }, /* DNSSEC Trust Authorities [Sam_Weiler][http://cameo.library.cmu.edu/][ Deploying DNSSEC Without a Signed Root. Technical Report 1999-19, Information Networking Institute, Carnegie Mellon University, April 2004.] 2005-12-13*/ { 32769, "DLV" }, /* DNSSEC Lookaside Validation (OBSOLETE) [RFC8749][RFC4431] */ }; @@ -133,6 +135,17 @@ static void rehash(int size); static void cache_hash(struct crec *crecp); +unsigned short rrtype(char *in) +{ + unsigned int i; + + for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++) + if (strcasecmp(in, typestr[i].name) == 0) + return typestr[i].type; + + return 0; +} + void next_uid(struct crec *crecp) { static unsigned int uid = 0; @@ -263,8 +276,8 @@ { if (!(crecp->flags & F_NEG)) { - if (crecp->flags & F_SRV) - blockdata_free(crecp->addr.srv.target); + if ((crecp->flags & F_RR) && (crecp->flags & F_KEYTAG)) + blockdata_free(crecp->addr.rrblock.rrdata); #ifdef HAVE_DNSSEC else if (crecp->flags & F_DNSKEY) blockdata_free(crecp->addr.key.keydata); @@ -412,18 +425,21 @@ { int i; unsigned int removed = 0; - struct crec *crecp, **up; + struct crec *crecp, *tmp, **up; for (i = 0; i < hash_size; i++) - for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next) - if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid) - { - *up = crecp->hash_next; - free(crecp); - removed++; - } - else - up = &crecp->hash_next; + for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = tmp) + { + tmp = crecp->hash_next; + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid) + { + *up = tmp; + free(crecp); + removed++; + } + else + up = &crecp->hash_next; + } return removed; } @@ -457,9 +473,20 @@ { if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name)) { + int rrmatch = 0; + if (crecp->flags & flags & F_RR) + { + unsigned short rrc = (crecp->flags & F_KEYTAG) ? crecp->addr.rrblock.rrtype : crecp->addr.rrdata.rrtype; + unsigned short rra = (flags & F_KEYTAG) ? addr->rrblock.rrtype : addr->rrdata.rrtype; + + if (rrc == rra) + rrmatch = 1; + } + /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */ - if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) || - (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS)))) + if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_NXDOMAIN)) || + (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))) || + rrmatch) { if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) return crecp; @@ -606,8 +633,8 @@ if (insert_error) return NULL; - /* we don't cache zero-TTL records. */ - if (ttl == 0) + /* we don't cache zero-TTL records unless we're doing stale-caching. */ + if (daemon->cache_max_expiry == 0 && ttl == 0) { insert_error = 1; return NULL; @@ -775,14 +802,13 @@ read_write(daemon->pipe_to_parent, (unsigned char *)name, m, 0); read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0); read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), 0); - - if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV)) - read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0); - if (flags & F_SRV) + read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0); + + if (flags & F_RR) { - /* A negative SRV entry is possible and has no data, obviously. */ - if (!(flags & F_NEG)) - blockdata_write(new_chain->addr.srv.target, new_chain->addr.srv.targetlen, daemon->pipe_to_parent); + /* A negative RR entry is possible and has no data, obviously. */ + if (!(flags & F_NEG) && (flags & F_KEYTAG)) + blockdata_write(new_chain->addr.rrblock.rrdata, new_chain->addr.rrblock.datalen, daemon->pipe_to_parent); } #ifdef HAVE_DNSSEC if (flags & F_DNSKEY) @@ -808,7 +834,18 @@ if (daemon->pipe_to_parent != -1) { ssize_t m = -1; + + read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0); + +#ifdef HAVE_DNSSEC + /* Sneak out possibly updated crypto HWM values. */ + m = daemon->metrics[METRIC_CRYPTO_HWM]; + read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0); + m = daemon->metrics[METRIC_SIG_FAIL_HWM]; read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0); + m = daemon->metrics[METRIC_WORK_HWM]; + read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), 0); +#endif } new_chain = NULL; @@ -827,7 +864,7 @@ cache_start_insert(); - while(1) + while (1) { if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1)) @@ -835,47 +872,36 @@ if (m == -1) { +#ifdef HAVE_DNSSEC + /* Sneak in possibly updated crypto HWM. */ + if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1)) + return 0; + if (m > daemon->metrics[METRIC_CRYPTO_HWM]) + daemon->metrics[METRIC_CRYPTO_HWM] = m; + if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1)) + return 0; + if (m > daemon->metrics[METRIC_SIG_FAIL_HWM]) + daemon->metrics[METRIC_SIG_FAIL_HWM] = m; + if (!read_write(fd, (unsigned char *)&m, sizeof(m), 1)) + return 0; + if (m > daemon->metrics[METRIC_WORK_HWM]) + daemon->metrics[METRIC_WORK_HWM] = m; +#endif cache_end_insert(); return 1; } if (!read_write(fd, (unsigned char *)daemon->namebuff, m, 1) || !read_write(fd, (unsigned char *)&ttd, sizeof(ttd), 1) || - !read_write(fd, (unsigned char *)&flags, sizeof(flags), 1)) + !read_write(fd, (unsigned char *)&flags, sizeof(flags), 1) || + !read_write(fd, (unsigned char *)&addr, sizeof(addr), 1)) return 0; daemon->namebuff[m] = 0; ttl = difftime(ttd, now); - if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV)) - { - unsigned short class = C_IN; - - if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1)) - return 0; - - if ((flags & F_SRV) && !(flags & F_NEG) && !(addr.srv.target = blockdata_read(fd, addr.srv.targetlen))) - return 0; - -#ifdef HAVE_DNSSEC - if (flags & F_DNSKEY) - { - if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) || - !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))) - return 0; - } - else if (flags & F_DS) - { - if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) || - (!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))) - return 0; - } -#endif - - crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags); - } - else if (flags & F_CNAME) + if (flags & F_CNAME) { struct crec *newc = really_insert(daemon->namebuff, NULL, C_IN, now, ttl, flags); /* This relies on the fact that the target of a CNAME immediately precedes @@ -883,11 +909,11 @@ the order reversal on the new_chain. */ if (newc) { - newc->addr.cname.is_name_ptr = 0; - - if (!crecp) - newc->addr.cname.target.cache = NULL; - else + newc->addr.cname.is_name_ptr = 0; + + if (!crecp) + newc->addr.cname.target.cache = NULL; + else { next_uid(crecp); newc->addr.cname.target.cache = crecp; @@ -895,6 +921,29 @@ } } } + else + { + unsigned short class = C_IN; + + if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG) + && !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen))) + return 0; +#ifdef HAVE_DNSSEC + if (flags & F_DNSKEY) + { + if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) || + !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))) + return 0; + } + else if (flags & F_DS) + { + if (!read_write(fd, (unsigned char *)&class, sizeof(class), 1) || + (!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))) + return 0; + } +#endif + crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags); + } } } @@ -1587,7 +1636,7 @@ if (!is_outdated_cname_pointer(crecp) && (crecp->flags & F_FORWARD) && (crecp->flags & type) && - !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS)) && + !(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_DNSKEY | F_DS | F_RR)) && hostname_isequal(name, cache_get_name(crecp))) { *up = crecp->hash_next; @@ -1644,7 +1693,7 @@ if (crecp) { - crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE); + crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_RR | F_DNSKEY | F_DS | F_REVERSE); if (!(crecp->flags & F_IMMORTAL)) crecp->ttd = source->ttd; crecp->name.namep = name; @@ -1776,21 +1825,27 @@ p = buff; *a = 0; - if (strlen(n) == 0 && !(cache->flags & F_REVERSE)) - n = ""; + + if (cache->flags & F_REVERSE) + { + if ((cache->flags & F_NEG)) + n = ""; + } + else + { + if (strlen(n) == 0) + n = ""; + } + p += sprintf(p, "%-30.30s ", sanitise(n)); if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache)) a = sanitise(cache_get_cname_target(cache)); - else if ((cache->flags & F_SRV) && !(cache->flags & F_NEG)) + else if (cache->flags & F_RR) { - int targetlen = cache->addr.srv.targetlen; - ssize_t len = sprintf(a, "%u %u %u ", cache->addr.srv.priority, - cache->addr.srv.weight, cache->addr.srv.srvport); - - if (targetlen > (40 - len)) - targetlen = 40 - len; - blockdata_retrieve(cache->addr.srv.target, targetlen, a + len); - a[len + targetlen] = 0; + if (cache->flags & F_KEYTAG) + sprintf(a, "%s", querystr(NULL, cache->addr.rrblock.rrtype)); + else + sprintf(a, "%s", querystr(NULL, cache->addr.rrdata.rrtype)); } #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) @@ -1818,8 +1873,8 @@ t = "6"; else if (cache->flags & F_CNAME) t = "C"; - else if (cache->flags & F_SRV) - t = "V"; + else if (cache->flags & F_RR) + t = "T"; #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) t = "S"; @@ -1864,9 +1919,19 @@ #ifdef HAVE_AUTH my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]); #endif +#ifdef HAVE_DNSSEC + my_syslog(LOG_INFO, _("DNSSEC per-query subqueries HWM %u"), daemon->metrics[METRIC_WORK_HWM]); + my_syslog(LOG_INFO, _("DNSSEC per-query crypto work HWM %u"), daemon->metrics[METRIC_CRYPTO_HWM]); + my_syslog(LOG_INFO, _("DNSSEC per-RRSet signature fails HWM %u"), daemon->metrics[METRIC_SIG_FAIL_HWM]); +#endif blockdata_report(); - + my_syslog(LOG_INFO, _("child processes for TCP requests: in use %zu, highest since last SIGUSR1 %zu, max allowed %zu."), + daemon->metrics[METRIC_TCP_CONNECTIONS], + daemon->max_procs_used, + daemon->max_procs); + daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS]; + /* sum counts from different records for same server */ for (serv = daemon->servers; serv; serv = serv->next) serv->flags &= ~SERV_MARK; @@ -2018,15 +2083,21 @@ case EDE_NO_AUTH: return "no reachable authority"; case EDE_NETERR: return "network error"; case EDE_INVALID_DATA: return "invalid data"; + case EDE_SIG_E_B_V: return "signature expired before valid"; + case EDE_TOO_EARLY: return "too early"; + case EDE_UNS_NS3_ITER: return "unsupported NSEC3 iterations value"; + case EDE_UNABLE_POLICY: return "uanble to conform to policy"; + case EDE_SYNTHESIZED: return "synthesized"; default: return "unknown"; } } void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type) { - char *source, *dest = arg; + char *source, *dest; char *verb = "is"; char *extra = ""; + char *gap = " "; char portstring[7]; /* space for # */ if (!option_bool(OPT_LOG)) @@ -2036,6 +2107,8 @@ if (!(flags & (F_SERVER | F_IPSET)) && type > 0) arg = querystr(arg, type); + dest = arg; + #ifdef HAVE_DNSSEC if ((flags & F_DNSSECOK) && option_bool(OPT_EXTRALOG)) extra = " (DNSSEC signed)"; @@ -2047,7 +2120,14 @@ { dest = daemon->addrbuff; - if (flags & F_KEYTAG) + if (flags & F_RR) + { + if (flags & F_KEYTAG) + dest = querystr(NULL, addr->rrblock.rrtype); + else + dest = querystr(NULL, addr->rrdata.rrtype); + } + else if (flags & F_KEYTAG) sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest); else if (flags & F_RCODE) { @@ -2104,8 +2184,6 @@ } else if (flags & F_CNAME) dest = ""; - else if (flags & F_SRV) - dest = ""; else if (flags & F_RRNAME) dest = arg; @@ -2156,19 +2234,21 @@ else source = "cached"; - if (name && !name[0]) + if (!name) + gap = name = ""; + else if (!name[0]) name = "."; - + if (option_bool(OPT_EXTRALOG)) { if (flags & F_NOEXTRA) - my_syslog(LOG_INFO, "%u %s %s %s %s%s", daemon->log_display_id, source, name, verb, dest, extra); + my_syslog(LOG_INFO, "%u %s %s%s%s %s%s", daemon->log_display_id, source, name, gap, verb, dest, extra); else { int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2); - my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s%s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest, extra); + my_syslog(LOG_INFO, "%u %s/%u %s %s%s%s %s%s", daemon->log_display_id, daemon->addrbuff2, port, source, name, gap, verb, dest, extra); } } else - my_syslog(LOG_INFO, "%s %s %s %s%s", source, name, verb, dest, extra); + my_syslog(LOG_INFO, "%s %s%s%s %s%s", source, name, gap, verb, dest, extra); } diff -Nru dnsmasq-2.89/src/config.h dnsmasq-2.90/src/config.h --- dnsmasq-2.89/src/config.h 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/config.h 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,14 +15,17 @@ */ #define FTABSIZ 150 /* max number of outstanding requests (default) */ -#define MAX_PROCS 20 /* max no children for TCP requests */ +#define MAX_PROCS 20 /* default max no children for TCP requests */ #define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */ #define TCP_MAX_QUERIES 100 /* Maximum number of queries per incoming TCP connection */ #define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */ -#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */ +#define EDNS_PKTSZ 1232 /* default max EDNS.0 UDP packet from from /dnsflagday.net/2020 */ #define SAFE_PKTSZ 1232 /* "go anywhere" UDP packet size, see https://dnsflagday.net/2020/ */ #define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */ -#define DNSSEC_WORK 50 /* Max number of queries to validate one question */ +#define DNSSEC_LIMIT_WORK 40 /* Max number of queries to validate one question */ +#define DNSSEC_LIMIT_SIG_FAIL 20 /* Number of signature that can fail to validate in one answer */ +#define DNSSEC_LIMIT_CRYPTO 200 /* max no. of crypto operations to validate one query. */ +#define DNSSEC_LIMIT_NSEC3_ITERS 150 /* Max. number if iterations allowed in NSEC3 record. */ #define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */ #define SMALL_PORT_RANGE 30 /* If DNS port range is smaller than this, use different allocation. */ #define FORWARD_TEST 50 /* try all servers every 50 queries */ diff -Nru dnsmasq-2.89/src/conntrack.c dnsmasq-2.90/src/conntrack.c --- dnsmasq-2.89/src/conntrack.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/conntrack.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/crypto.c dnsmasq-2.90/src/crypto.c --- dnsmasq-2.89/src/crypto.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/crypto.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/dbus.c dnsmasq-2.90/src/dbus.c --- dnsmasq-2.89/src/dbus.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dbus.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -52,6 +52,12 @@ " \n" " \n" " \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" " \n" " \n" " \n" @@ -100,6 +106,7 @@ "\n"; static char *introspection_xml = NULL; +static int watches_modified = 0; struct watch { DBusWatch *watch; @@ -121,6 +128,7 @@ w->watch = watch; w->next = daemon->watches; daemon->watches = w; + watches_modified++; (void)data; /* no warning */ return TRUE; @@ -128,7 +136,7 @@ static void remove_watch(DBusWatch *watch, void *data) { - struct watch **up, *w, *tmp; + struct watch **up, *w, *tmp; for (up = &(daemon->watches), w = daemon->watches; w; w = tmp) { @@ -137,6 +145,7 @@ { *up = tmp; free(w); + watches_modified++; } else up = &(w->next); @@ -817,6 +826,28 @@ { reply = dbus_set_bool(message, OPT_FILTER, "filterwin2k"); } + else if (strcmp(method, "SetFilterA") == 0) + { + static int done = 0; + static struct rrlist list = { T_A, NULL }; + + if (!done) + { + list.next = daemon->filter_rr; + daemon->filter_rr = &list; + } + } + else if (strcmp(method, "SetFilterAAAA") == 0) + { + static int done = 0; + static struct rrlist list = { T_AAAA, NULL }; + + if (!done) + { + list.next = daemon->filter_rr; + daemon->filter_rr = &list; + } + } else if (strcmp(method, "SetLocaliseQueriesOption") == 0) { reply = dbus_set_bool(message, OPT_LOCALISE, "localise-queries"); @@ -927,41 +958,53 @@ { unsigned int flags = dbus_watch_get_flags(w->watch); int fd = dbus_watch_get_unix_fd(w->watch); + int poll_flags = POLLERR; if (flags & DBUS_WATCH_READABLE) - poll_listen(fd, POLLIN); - + poll_flags |= POLLIN; if (flags & DBUS_WATCH_WRITABLE) - poll_listen(fd, POLLOUT); + poll_flags |= POLLOUT; - poll_listen(fd, POLLERR); + poll_listen(fd, poll_flags); } } -void check_dbus_listeners() +static int check_dbus_watches() { - DBusConnection *connection = (DBusConnection *)daemon->dbus; struct watch *w; + watches_modified = 0; for (w = daemon->watches; w; w = w->next) if (dbus_watch_get_enabled(w->watch)) { unsigned int flags = 0; int fd = dbus_watch_get_unix_fd(w->watch); - - if (poll_check(fd, POLLIN)) + int poll_flags = poll_check(fd, POLLIN|POLLOUT|POLLERR); + + if ((poll_flags & POLLIN) != 0) flags |= DBUS_WATCH_READABLE; - - if (poll_check(fd, POLLOUT)) + if ((poll_flags & POLLOUT) != 0) flags |= DBUS_WATCH_WRITABLE; - - if (poll_check(fd, POLLERR)) + if ((poll_flags & POLLERR) != 0) flags |= DBUS_WATCH_ERROR; if (flags != 0) - dbus_watch_handle(w->watch, flags); + { + dbus_watch_handle(w->watch, flags); + if (watches_modified) + return 0; + } } + return 1; +} + +void check_dbus_listeners() +{ + DBusConnection *connection = (DBusConnection *)daemon->dbus; + + while (!check_dbus_watches()) ; + if (connection) { dbus_connection_ref (connection); diff -Nru dnsmasq-2.89/src/dhcp-common.c dnsmasq-2.90/src/dhcp-common.c --- dnsmasq-2.89/src/dhcp-common.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dhcp-common.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -553,11 +553,11 @@ return NULL; for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) - if (if_tmp->name && (!if_tmp->used || strchr(if_tmp->name, '*'))) + if (if_tmp->name && (!(if_tmp->flags & INAME_USED) || strchr(if_tmp->name, '*'))) return NULL; for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next) - if (iface->dhcp_ok) + if (iface->dhcp4_ok || iface->dhcp6_ok) { if (!found) found = iface; @@ -838,7 +838,7 @@ for (i = 0, j = 0; i < opt_len && j < buf_len ; i++) { char c = val[i]; - if (isprint((int)c)) + if (isprint((unsigned char)c)) buf[j++] = c; } #ifdef HAVE_DHCP6 @@ -852,7 +852,7 @@ for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++) { char c = val[k]; - if (isprint((int)c)) + if (isprint((unsigned char)c)) buf[j++] = c; } i = l; @@ -873,7 +873,7 @@ for (k = 0; k < len && j < buf_len; k++) { char c = *p++; - if (isprint((int)c)) + if (isprint((unsigned char)c)) buf[j++] = c; } i += len +2; diff -Nru dnsmasq-2.89/src/dhcp-protocol.h dnsmasq-2.90/src/dhcp-protocol.h --- dnsmasq-2.89/src/dhcp-protocol.h 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dhcp-protocol.h 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/dhcp.c dnsmasq-2.90/src/dhcp.c --- dnsmasq-2.89/src/dhcp.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dhcp.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -297,7 +297,7 @@ } for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) - if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) + if (tmp->name && (tmp->flags & INAME_4) && wildcard_match(tmp->name, ifr.ifr_name)) return; /* unlinked contexts/relays are marked by context->current == context */ @@ -916,14 +916,14 @@ lineno++; - while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1])) + while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1])) buff[strlen(buff)-1] = 0; if ((*buff == '#') || (*buff == '+') || (*buff == 0)) continue; - for (ip = buff; *ip && !isspace((int)*ip); ip++); - for(; *ip && isspace((int)*ip); ip++) + for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++); + for(; *ip && isspace((unsigned char)*ip); ip++) *ip = 0; if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN) { diff -Nru dnsmasq-2.89/src/dhcp6-protocol.h dnsmasq-2.90/src/dhcp6-protocol.h --- dnsmasq-2.89/src/dhcp6-protocol.h 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dhcp6-protocol.h 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/dhcp6.c dnsmasq-2.90/src/dhcp6.c --- dnsmasq-2.89/src/dhcp6.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dhcp6.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -92,7 +92,7 @@ struct iface_param parm; struct cmsghdr *cmptr; struct msghdr msg; - int if_index = 0; + uint32_t if_index = 0; union { struct cmsghdr align; /* this ensures alignment */ char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; @@ -118,11 +118,6 @@ if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1) return; -#ifdef HAVE_DUMPFILE - dump_packet_udp(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz, - (union mysockaddr *)&from, NULL, daemon->dhcp6fd); -#endif - for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) { @@ -138,6 +133,34 @@ if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name)) return; + +#ifdef HAVE_LINUX_NETWORK + /* This works around a possible Linux kernel bug when using interfaces + enslaved to a VRF. The scope_id in the source address gets set + to the index of the VRF interface, not the slave. Fortunately, + the interface index returned by packetinfo is correct so we use + that instead. Log this once, so if it triggers in other circumstances + we've not anticipated and breaks things, we get some clues. */ + if (from.sin6_scope_id != if_index) + { + static int logged = 0; + + if (!logged) + { + my_syslog(MS_DHCP | LOG_WARNING, + _("Working around kernel bug: faulty source address scope for VRF slave %s"), + ifr.ifr_name); + logged = 1; + } + + from.sin6_scope_id = if_index; + } +#endif + +#ifdef HAVE_DUMPFILE + dump_packet_udp(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz, + (union mysockaddr *)&from, NULL, daemon->dhcp6fd); +#endif if (relay_reply6(&from, sz, ifr.ifr_name)) { @@ -159,7 +182,8 @@ return; for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) - if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) + if (tmp->name && (tmp->flags & INAME_6) && + wildcard_match(tmp->name, ifr.ifr_name)) return; parm.current = NULL; diff -Nru dnsmasq-2.89/src/dns-protocol.h dnsmasq-2.90/src/dns-protocol.h --- dnsmasq-2.89/src/dns-protocol.h 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dns-protocol.h 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -112,8 +112,11 @@ #define EDE_NO_AUTH 22 /* No Reachable Authority */ #define EDE_NETERR 23 /* Network error */ #define EDE_INVALID_DATA 24 /* Invalid Data */ - - +#define EDE_SIG_E_B_V 25 /* Signature Expired before Valid */ +#define EDE_TOO_EARLY 26 /* To Early */ +#define EDE_UNS_NS3_ITER 27 /* Unsupported NSEC3 Iterations Value */ +#define EDE_UNABLE_POLICY 28 /* Unable to conform to policy */ +#define EDE_SYNTHESIZED 29 /* Synthesized */ struct dns_header { diff -Nru dnsmasq-2.89/src/dnsmasq.c dnsmasq-2.90/src/dnsmasq.c --- dnsmasq-2.89/src/dnsmasq.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dnsmasq.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,12 +30,14 @@ static volatile int pipewrite; static void set_dns_listeners(void); +static void set_tftp_listeners(void); static void check_dns_listeners(time_t now); static void sig_handler(int sig); static void async_event(int pipe, time_t now); static void fatal_event(struct event_desc *ev, char *msg); static int read_event(int fd, struct event_desc *evp, char **msg); static void poll_resolv(int force, int do_reload, time_t now); +static void tcp_init(void); int main (int argc, char **argv) { @@ -125,29 +127,15 @@ { /* Note that both /000 and '.' are allowed within labels. These get represented in presentation format using NAME_ESCAPE as an escape - character when in DNSSEC mode. - In theory, if all the characters in a name were /000 or + character. In theory, if all the characters in a name were /000 or '.' or NAME_ESCAPE then all would have to be escaped, so the - presentation format would be twice as long as the spec. - - daemon->namebuff was previously allocated by the option-reading - code before we knew if we're in DNSSEC mode, so reallocate here. */ - free(daemon->namebuff); - daemon->namebuff = safe_malloc(MAXDNAME * 2); - daemon->keyname = safe_malloc(MAXDNAME * 2); - daemon->workspacename = safe_malloc(MAXDNAME * 2); + presentation format would be twice as long as the spec. */ + daemon->keyname = safe_malloc((MAXDNAME * 2) + 1); /* one char flag per possible RR in answer section (may get extended). */ daemon->rr_status_sz = 64; daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz); } #endif - -#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) - /* CONNTRACK UBUS code uses this buffer, so if not allocated above, - we need to allocate it here. */ - if (option_bool(OPT_CMARK_ALST_EN) && !daemon->workspacename) - daemon->workspacename = safe_malloc(MAXDNAME); -#endif #ifdef HAVE_DHCP if (!daemon->lease_file) @@ -378,6 +366,13 @@ if (!enumerate_interfaces(1) || !enumerate_interfaces(0)) die(_("failed to find list of interfaces: %s"), NULL, EC_MISC); + +#ifdef HAVE_DHCP + /* Determine lease FQDNs after enumerate_interfaces() call, since it needs + to call get_domain and that's only valid for some domain configs once we + have interface addresses. */ + lease_calc_fqdns(); +#endif if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND)) { @@ -385,7 +380,7 @@ if (!option_bool(OPT_CLEVERBIND)) for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) - if (if_tmp->name && !if_tmp->used) + if (if_tmp->name && !(if_tmp->flags & INAME_USED)) die(_("unknown interface %s"), if_tmp->name, EC_BADNET); #if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP) @@ -421,11 +416,13 @@ daemon->numrrand = max_fd/3; /* safe_malloc returns zero'd memory */ daemon->randomsocks = safe_malloc(daemon->numrrand * sizeof(struct randfd)); + + tcp_init(); } #ifdef HAVE_INOTIFY - if ((daemon->port != 0 || daemon->dhcp || daemon->doing_dhcp6) - && (!option_bool(OPT_NO_RESOLV) || daemon->dynamic_dirs)) + if ((daemon->port != 0 && !option_bool(OPT_NO_RESOLV)) || + daemon->dynamic_dirs) inotify_dnsmasq_init(); else daemon->inotifyfd = -1; @@ -863,6 +860,8 @@ if (option_bool(OPT_LOCAL_SERVICE)) my_syslog(LOG_INFO, _("DNS service limited to local subnets")); + else if (option_bool(OPT_LOCALHOST_SERVICE)) + my_syslog(LOG_INFO, _("DNS service limited to localhost")); } my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts); @@ -941,7 +940,7 @@ if (!option_bool(OPT_NOWILD)) for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next) - if (if_tmp->name && !if_tmp->used) + if (if_tmp->name && !(if_tmp->flags & INAME_USED)) my_syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name); if (daemon->port != 0 && option_bool(OPT_NO_RESOLV)) @@ -1049,8 +1048,10 @@ pid = getpid(); daemon->pipe_to_parent = -1; - for (i = 0; i < MAX_PROCS; i++) - daemon->tcp_pipes[i] = -1; + + if (daemon->port != 0) + for (i = 0; i < daemon->max_procs; i++) + daemon->tcp_pipes[i] = -1; #ifdef HAVE_INOTIFY /* Using inotify, have to select a resolv file at startup */ @@ -1073,7 +1074,12 @@ (timeout == -1 || timeout > 1000)) timeout = 1000; - set_dns_listeners(); + if (daemon->port != 0) + set_dns_listeners(); + +#ifdef HAVE_TFTP + set_tftp_listeners(); +#endif #ifdef HAVE_DBUS if (option_bool(OPT_DBUS)) @@ -1258,8 +1264,9 @@ check_ubus_listeners(); } #endif - - check_dns_listeners(now); + + if (daemon->port != 0) + check_dns_listeners(now); #ifdef HAVE_TFTP check_tftp_listeners(now); @@ -1525,10 +1532,15 @@ if (errno != EINTR) break; } - else - for (i = 0 ; i < MAX_PROCS; i++) + else if (daemon->port != 0) + for (i = 0 ; i < daemon->max_procs; i++) if (daemon->tcp_pids[i] == p) - daemon->tcp_pids[i] = 0; + { + daemon->tcp_pids[i] = 0; + /* tcp_pipes == -1 && tcp_pids == 0 required to free slot */ + if (daemon->tcp_pipes[i] == -1) + daemon->metrics[METRIC_TCP_CONNECTIONS]--; + } break; #if defined(HAVE_SCRIPT) @@ -1590,9 +1602,10 @@ case EVENT_TERM: /* Knock all our children on the head. */ - for (i = 0; i < MAX_PROCS; i++) - if (daemon->tcp_pids[i] != 0) - kill(daemon->tcp_pids[i], SIGALRM); + if (daemon->port != 0) + for (i = 0; i < daemon->max_procs; i++) + if (daemon->tcp_pids[i] != 0) + kill(daemon->tcp_pids[i], SIGALRM); #if defined(HAVE_SCRIPT) && defined(HAVE_DHCP) /* handle pending lease transitions */ @@ -1737,23 +1750,33 @@ #endif } -static void set_dns_listeners(void) -{ - struct serverfd *serverfdp; - struct listener *listener; - struct randfd_list *rfl; - int i; - #ifdef HAVE_TFTP +static void set_tftp_listeners(void) +{ int tftp = 0; struct tftp_transfer *transfer; + struct listener *listener; + if (!option_bool(OPT_SINGLE_PORT)) for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) { tftp++; poll_listen(transfer->sockfd, POLLIN); } + + for (listener = daemon->listeners; listener; listener = listener->next) + /* tftp == 0 in single-port mode. */ + if (tftp <= daemon->tftp_max && listener->tftpfd != -1) + poll_listen(listener->tftpfd, POLLIN); +} #endif + +static void set_dns_listeners(void) +{ + struct serverfd *serverfdp; + struct listener *listener; + struct randfd_list *rfl; + int i; for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) poll_listen(serverfdp->fd, POLLIN); @@ -1767,7 +1790,7 @@ poll_listen(rfl->rfd->fd, POLLIN); /* check to see if we have free tcp process slots. */ - for (i = MAX_PROCS - 1; i >= 0; i--) + for (i = daemon->max_procs - 1; i >= 0; i--) if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1) break; @@ -1782,16 +1805,10 @@ we'll be called again when a slot becomes available. */ if (listener->tcpfd != -1 && i >= 0) poll_listen(listener->tcpfd, POLLIN); - -#ifdef HAVE_TFTP - /* tftp == 0 in single-port mode. */ - if (tftp <= daemon->tftp_max && listener->tftpfd != -1) - poll_listen(listener->tftpfd, POLLIN); -#endif } if (!option_bool(OPT_DEBUG)) - for (i = 0; i < MAX_PROCS; i++) + for (i = 0; i < daemon->max_procs; i++) if (daemon->tcp_pipes[i] != -1) poll_listen(daemon->tcp_pipes[i], POLLIN); } @@ -1826,13 +1843,16 @@ to free the process slot. Once the child process has gone, poll() returns POLLHUP, not POLLIN, so have to check for both here. */ if (!option_bool(OPT_DEBUG)) - for (i = 0; i < MAX_PROCS; i++) + for (i = 0; i < daemon->max_procs; i++) if (daemon->tcp_pipes[i] != -1 && poll_check(daemon->tcp_pipes[i], POLLIN | POLLHUP) && !cache_recv_insert(now, daemon->tcp_pipes[i])) { close(daemon->tcp_pipes[i]); daemon->tcp_pipes[i] = -1; + /* tcp_pipes == -1 && tcp_pids == 0 required to free slot */ + if (daemon->tcp_pids[i] == 0) + daemon->metrics[METRIC_TCP_CONNECTIONS]--; } for (listener = daemon->listeners; listener; listener = listener->next) @@ -1840,17 +1860,12 @@ if (listener->fd != -1 && poll_check(listener->fd, POLLIN)) receive_query(listener, now); -#ifdef HAVE_TFTP - if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN)) - tftp_request(listener, now); -#endif - /* check to see if we have a free tcp process slot. Note that we can't assume that because we had at least one a poll() time, that we still do. There may be more waiting connections after poll() returns then free process slots. */ - for (i = MAX_PROCS - 1; i >= 0; i--) + for (i = daemon->max_procs - 1; i >= 0; i--) if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1) break; @@ -1966,6 +1981,9 @@ /* i holds index of free slot */ daemon->tcp_pids[i] = p; daemon->tcp_pipes[i] = pipefd[0]; + daemon->metrics[METRIC_TCP_CONNECTIONS]++; + if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used) + daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS]; } close(confd); @@ -2141,7 +2159,11 @@ poll_reset(); if (fd != -1) poll_listen(fd, POLLIN); - set_dns_listeners(); + if (daemon->port != 0) + set_dns_listeners(); +#ifdef HAVE_TFTP + set_tftp_listeners(); +#endif set_log_writer(); #ifdef HAVE_DHCP6 @@ -2159,7 +2181,8 @@ now = dnsmasq_time(); check_log_writer(0); - check_dns_listeners(now); + if (daemon->port != 0) + check_dns_listeners(now); #ifdef HAVE_DHCP6 if (daemon->doing_ra && poll_check(daemon->icmp6fd, POLLIN)) @@ -2192,3 +2215,9 @@ return 0; } #endif /* HAVE_DHCP */ + +void tcp_init(void) +{ + daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t)); + daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int)); +} diff -Nru dnsmasq-2.89/src/dnsmasq.h dnsmasq-2.90/src/dnsmasq.h --- dnsmasq-2.89/src/dnsmasq.h 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dnsmasq.h 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,7 +14,7 @@ along with this program. If not, see . */ -#define COPYRIGHT "Copyright (c) 2000-2022 Simon Kelley" +#define COPYRIGHT "Copyright (c) 2000-2024 Simon Kelley" /* We do defines that influence behavior of stdio.h, so complain if included too early. */ @@ -276,12 +276,12 @@ #define OPT_UMBRELLA_DEVID 64 #define OPT_CMARK_ALST_EN 65 #define OPT_QUIET_TFTP 66 -#define OPT_FILTER_A 67 -#define OPT_FILTER_AAAA 68 -#define OPT_STRIP_ECS 69 -#define OPT_STRIP_MAC 70 -#define OPT_NORR 71 -#define OPT_NO_IDENT 72 +#define OPT_STRIP_ECS 67 +#define OPT_STRIP_MAC 68 +#define OPT_NORR 69 +#define OPT_NO_IDENT 70 +#define OPT_CACHE_RR 71 +#define OPT_LOCALHOST_SERVICE 72 #define OPT_LAST 73 #define OPTION_BITS (sizeof(unsigned int)*8) @@ -325,17 +325,28 @@ unsigned char algo; unsigned char digest; } ds; - struct { - struct blockdata *target; - unsigned short targetlen, srvport, priority, weight; - } srv; /* for log_query */ struct { unsigned short keytag, algo, digest, rcode; int ede; } log; + /* for arbitrary RR record stored in block */ + struct { + unsigned short rrtype; + unsigned short datalen; + struct blockdata *rrdata; + } rrblock; + /* for arbitrary RR record small enough to go in addr. + NOTE: rrblock and rrdata are discriminated by the F_KEYTAG bit + in the cache flags. */ + struct datablock { + unsigned short rrtype; + unsigned char datalen; /* also length of SOA in negative records. */ + char data[]; + } rrdata; }; +#define RR_IMDATALEN (sizeof(union all_addr) - offsetof(struct datablock, data)) struct bogus_addr { int is6, prefix; @@ -512,7 +523,7 @@ #define F_NOEXTRA (1u<<27) #define F_DOMAINSRV (1u<<28) #define F_RCODE (1u<<29) -#define F_SRV (1u<<30) +#define F_RR (1u<<30) #define F_STALE (1u<<31) #define UID_NONE 0 @@ -638,7 +649,8 @@ struct irec { union mysockaddr addr; struct in_addr netmask; /* only valid for IPv4 */ - int tftp_ok, dhcp_ok, mtu, done, warned, dad, dns_auth, index, multicast_done, found, label; + int tftp_ok, dhcp4_ok, dhcp6_ok, mtu, done, warned, dad; + int dns_auth, index, multicast_done, found, label; char *name; struct irec *next; }; @@ -654,10 +666,19 @@ struct iname { char *name; union mysockaddr addr; - int used; + int flags; struct iname *next; }; +#define INAME_USED 1 +#define INAME_4 2 +#define INAME_6 4 + +struct rrlist { + unsigned short rr; + struct rrlist *next; +}; + /* subnet parameters from command line */ struct mysubnet { union mysockaddr addr; @@ -736,6 +757,9 @@ #define DNSSEC_FAIL_NONSEC 0x0040 /* No NSEC */ #define DNSSEC_FAIL_NODSSUP 0x0080 /* no supported DS algo. */ #define DNSSEC_FAIL_NOKEY 0x0100 /* no DNSKEY */ +#define DNSSEC_FAIL_NSEC3_ITERS 0x0200 /* too many iterations in NSEC3 */ +#define DNSSEC_FAIL_BADPACKET 0x0400 /* bad packet */ +#define DNSSEC_FAIL_WORK 0x0800 /* too much crypto */ #define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b)) @@ -773,7 +797,7 @@ struct blockdata *stash; /* Saved reply, whilst we validate */ size_t stash_len; #ifdef HAVE_DNSSEC - int class, work_counter; + int class, work_counter, validate_counter; struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */ struct frec *next_dependent; /* list of above. */ struct frec *blocking_query; /* Query which is blocking us. */ @@ -810,6 +834,12 @@ #define LEASE_HAVE_HWADDR 128 /* Have set hwaddress */ #define LEASE_EXP_CHANGED 256 /* Lease expiry time changed */ +#define LIMIT_SIG_FAIL 0 +#define LIMIT_CRYPTO 1 +#define LIMIT_WORK 2 +#define LIMIT_NSEC3_ITERS 3 +#define LIMIT_MAX 4 + struct dhcp_lease { int clid_len; /* length of client identifier */ unsigned char *clid; /* clientid */ @@ -1123,6 +1153,7 @@ struct naptr *naptr; struct txt_record *txt, *rr; struct ptr_record *ptr; + struct rrlist *cache_rr, *filter_rr; struct host_record *host_records, *host_records_tail; struct cname *cnames; struct auth_zone *auth_zones; @@ -1211,16 +1242,14 @@ char *packet; /* packet buffer */ int packet_buff_sz; /* size of above */ char *namebuff; /* MAXDNAME size buffer */ -#if (defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)) || defined(HAVE_DNSSEC) - /* CONNTRACK UBUS code uses this buffer, as well as DNSSEC code. */ char *workspacename; -#endif #ifdef HAVE_DNSSEC char *keyname; /* MAXDNAME size buffer */ unsigned long *rr_status; /* ceiling in TTL from DNSSEC or zero for insecure */ int rr_status_sz; int dnssec_no_time_check; int back_to_the_future; + int limit[LIMIT_MAX]; #endif struct frec *frec_list; struct frec_src *free_frec_src; @@ -1231,8 +1260,8 @@ struct server *srv_save; /* Used for resend on DoD */ size_t packet_len; /* " " */ int fd_save; /* " " */ - pid_t tcp_pids[MAX_PROCS]; - int tcp_pipes[MAX_PROCS]; + pid_t *tcp_pids; + int *tcp_pipes; int pipe_to_parent; int numrrand; struct randfd *randomsocks; @@ -1292,6 +1321,8 @@ /* file for packet dumps. */ int dumpfd; #endif + int max_procs; + uint max_procs_used; } *daemon; struct server_details { @@ -1304,6 +1335,7 @@ /* cache.c */ void cache_init(void); +unsigned short rrtype(char *in); void next_uid(struct crec *crecp); void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg, unsigned short type); char *record_source(unsigned int index); @@ -1337,6 +1369,8 @@ void blockdata_init(void); void blockdata_report(void); struct blockdata *blockdata_alloc(char *data, size_t len); +int blockdata_expand(struct blockdata *block, size_t oldlen, + char *data, size_t newlen); void *blockdata_retrieve(struct blockdata *block, size_t len, void *data); struct blockdata *blockdata_read(int fd, size_t len); void blockdata_write(struct blockdata *block, size_t len, int fd); @@ -1349,6 +1383,7 @@ int is_rev_synth(int flag, union all_addr *addr, char *name); /* rfc1035.c */ +int do_doctor(struct dns_header *header, size_t qlen, char *namebuff); int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, char *name, int isExtract, int extrabytes); unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes); @@ -1359,14 +1394,14 @@ void setup_reply(struct dns_header *header, unsigned int flags, int ede); int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, - int check_rebind, int no_cache_dnssec, int secure, int *doctored); + int check_rebind, int no_cache_dnssec, int secure); #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) void report_addresses(struct dns_header *header, size_t len, u32 mark); #endif size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now, int ad_reqd, int do_bit, int have_pseudoheader, - int *stale); + int *stale, int *filtered); int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, time_t now); int check_for_ignored_address(struct dns_header *header, size_t qlen); @@ -1390,10 +1425,12 @@ /* dnssec.c */ #ifdef HAVE_DNSSEC size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz); -int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); -int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); +int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, + char *keyname, int class, int *validate_count); +int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, + char *keyname, int class, int *validate_count); int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, - int check_unsigned, int *neganswer, int *nons, int *nsec_ttl); + int check_unsigned, int *neganswer, int *nons, int *nsec_ttl, int *validate_count); int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen); size_t filter_rrsigs(struct dns_header *header, size_t plen); int setup_timestamp(void); @@ -1418,6 +1455,7 @@ unsigned short rand16(void); u32 rand32(void); u64 rand64(void); +int rr_on_list(struct rrlist *list, unsigned short rr); int legal_hostname(char *name); char *canonicalise(char *in, int *nomem); unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit); @@ -1578,6 +1616,7 @@ int do_script_run(time_t now); void rerun_scripts(void); void lease_find_interfaces(time_t now); +void lease_calc_fqdns(void); #ifdef HAVE_SCRIPT void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim); @@ -1811,14 +1850,16 @@ int do_poll(int timeout); /* rrfilter.c */ -size_t rrfilter(struct dns_header *header, size_t plen, int mode); -u16 *rrfilter_desc(int type); +size_t rrfilter(struct dns_header *header, size_t *plen, int mode); +short *rrfilter_desc(int type); int expand_workspace(unsigned char ***wkspc, int *szp, int new); +int to_wire(char *name); +void from_wire(char *name); /* modes. */ #define RRFILTER_EDNS0 0 #define RRFILTER_DNSSEC 1 -#define RRFILTER_A 2 -#define RRFILTER_AAAA 3 +#define RRFILTER_CONF 2 + /* edns0.c */ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last); diff -Nru dnsmasq-2.89/src/dnssec.c dnsmasq-2.90/src/dnssec.c --- dnsmasq-2.89/src/dnssec.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dnssec.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,5 +1,5 @@ /* dnssec.c is Copyright (c) 2012 Giovanni Bajo - and Copyright (c) 2012-2020 Simon Kelley + and Copyright (c) 2012-2023 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,81 +24,6 @@ #define SERIAL_LT -1 #define SERIAL_GT 1 -/* Convert from presentation format to wire format, in place. - Also map UC -> LC. - Note that using extract_name to get presentation format - then calling to_wire() removes compression and maps case, - thus generating names in canonical form. - Calling to_wire followed by from_wire is almost an identity, - except that the UC remains mapped to LC. - - Note that both /000 and '.' are allowed within labels. These get - represented in presentation format using NAME_ESCAPE as an escape - character. In theory, if all the characters in a name were /000 or - '.' or NAME_ESCAPE then all would have to be escaped, so the - presentation format would be twice as long as the spec (1024). - The buffers are all declared as 2049 (allowing for the trailing zero) - for this reason. -*/ -static int to_wire(char *name) -{ - unsigned char *l, *p, *q, term; - int len; - - for (l = (unsigned char*)name; *l != 0; l = p) - { - for (p = l; *p != '.' && *p != 0; p++) - if (*p >= 'A' && *p <= 'Z') - *p = *p - 'A' + 'a'; - else if (*p == NAME_ESCAPE) - { - for (q = p; *q; q++) - *q = *(q+1); - (*p)--; - } - term = *p; - - if ((len = p - l) != 0) - memmove(l+1, l, len); - *l = len; - - p++; - - if (term == 0) - *p = 0; - } - - return l + 1 - (unsigned char *)name; -} - -/* Note: no compression allowed in input. */ -static void from_wire(char *name) -{ - unsigned char *l, *p, *last; - int len; - - for (last = (unsigned char *)name; *last != 0; last += *last+1); - - for (l = (unsigned char *)name; *l != 0; l += len+1) - { - len = *l; - memmove(l, l+1, len); - for (p = l; p < l + len; p++) - if (*p == '.' || *p == 0 || *p == NAME_ESCAPE) - { - memmove(p+1, p, 1 + last - p); - len++; - *p++ = NAME_ESCAPE; - (*p)++; - } - - l[len] = '.'; - } - - if ((char *)l != name) - *(l-1) = 0; -} - /* Input in presentation format */ static int count_labels(char *name) { @@ -225,7 +150,7 @@ On returning 0, the end has been reached. */ struct rdata_state { - u16 *desc; + short *desc; size_t c; unsigned char *end, *ip, *op; char *buff; @@ -246,7 +171,7 @@ { d = *(state->desc); - if (d == (u16)-1) + if (d == -1) { /* all the bytes to the end. */ if ((state->c = state->end - state->ip) != 0) @@ -294,7 +219,7 @@ /* Bubble sort the RRset into the canonical order. */ -static int sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int rrsetidx, +static int sort_rrset(struct dns_header *header, size_t plen, short *rr_desc, int rrsetidx, unsigned char **rrset, char *buff1, char *buff2) { int swap, i, j; @@ -331,7 +256,7 @@ is the identity function and we can compare the RRs directly. If not we compare the canonicalised RRs one byte at a time. */ - if (*rr_desc == (u16)-1) + if (*rr_desc == -1) { int rdmin = rdlen1 > rdlen2 ? rdlen2 : rdlen1; int cmp = memcmp(state1.ip, state2.ip, rdmin); @@ -499,12 +424,24 @@ return 1; } +int dec_counter(int *counter, char *message) +{ + if ((*counter)-- == 0) + { + my_syslog(LOG_WARNING, "limit exceeded: %s", message ? message : _("per-query crypto work")); + return 1; + } + + return 0; +} + /* Validate a single RRset (class, type, name) in the supplied DNS reply Return code: STAT_SECURE if it validates. STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion. (In this case *wildcard_out points to the "body" of the wildcard within name.) STAT_BOGUS signature is wrong, bad packet. + STAT_ABANDONED validation abandoned do to excess resource usage. STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname) STAT_NEED_DS need DS to complete validation (name is returned in keyname) @@ -519,12 +456,12 @@ */ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, int type, int sigidx, int rrsetidx, char *name, char *keyname, char **wildcard_out, struct blockdata *key, int keylen, - int algo_in, int keytag_in, unsigned long *ttl_out) + int algo_in, int keytag_in, unsigned long *ttl_out, int *validate_counter) { unsigned char *p; - int rdlen, j, name_labels, algo, labels, key_tag; + int rdlen, j, name_labels, algo, labels, key_tag, sig_fail_cnt; struct crec *crecp = NULL; - u16 *rr_desc = rrfilter_desc(type); + short *rr_desc = rrfilter_desc(type); u32 sig_expiration, sig_inception; int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP; @@ -542,7 +479,7 @@ rrsetidx = sort_rrset(header, plen, rr_desc, rrsetidx, rrset, daemon->workspacename, keyname); /* Now try all the sigs to try and find one which validates */ - for (j = 0; j limit[LIMIT_SIG_FAIL], j = 0; j update(ctx, 2, (unsigned char *)&len); @@ -729,9 +666,14 @@ if (key) { - if (algo_in == algo && keytag_in == key_tag && - verify(key, keylen, sig, sig_len, digest, hash->digest_size, algo)) - return STAT_SECURE; + if (algo_in == algo && keytag_in == key_tag) + { + if (dec_counter(validate_counter, NULL)) + return STAT_ABANDONED; + + if (verify(key, keylen, sig, sig_len, digest, hash->digest_size, algo)) + return STAT_SECURE; + } } else { @@ -739,9 +681,22 @@ for (; crecp; crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY)) if (crecp->addr.key.algo == algo && crecp->addr.key.keytag == key_tag && - crecp->uid == (unsigned int)class && - verify(crecp->addr.key.keydata, crecp->addr.key.keylen, sig, sig_len, digest, hash->digest_size, algo)) - return (labels < name_labels) ? STAT_SECURE_WILDCARD : STAT_SECURE; + crecp->uid == (unsigned int)class) + { + if (dec_counter(validate_counter, NULL)) + return STAT_ABANDONED; + + if (verify(crecp->addr.key.keydata, crecp->addr.key.keylen, sig, sig_len, digest, hash->digest_size, algo)) + return (labels < name_labels) ? STAT_SECURE_WILDCARD : STAT_SECURE; + + /* An attacker can waste a lot of our CPU by setting up a giant DNSKEY RRSET full of failing + keys, all of which we have to try. Since many failing keys is not likely for + a legitimate domain, set a limit on how many can fail. */ + if ((daemon->limit[LIMIT_SIG_FAIL] - (sig_fail_cnt + 1)) > (int)daemon->metrics[METRIC_SIG_FAIL_HWM]) + daemon->metrics[METRIC_SIG_FAIL_HWM] = daemon->limit[LIMIT_SIG_FAIL] - (sig_fail_cnt + 1); + if (dec_counter(&sig_fail_cnt, _("per-RRSet signature fails"))) + return STAT_ABANDONED; + } } } @@ -756,39 +711,45 @@ STAT_OK Done, key(s) in cache. STAT_BOGUS No DNSKEYs found, which can be validated with DS, or self-sign for DNSKEY RRset is not valid, bad packet. + STAT_ABANDONED resource exhaustion. STAT_NEED_DS DS records to validate a key not found, name in keyname - STAT_NEED_KEY DNSKEY records to validate a key not found, name in keyname */ -int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class) +int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, + char *keyname, int class, int *validate_counter) { - unsigned char *psave, *p = (unsigned char *)(header+1); + unsigned char *psave, *p = (unsigned char *)(header+1), *keyaddr; struct crec *crecp, *recp1; - int rc, j, qtype, qclass, rdlen, flags, algo, valid, keytag; + int rc, j, qtype, qclass, rdlen, flags, algo, keytag, sigcnt, rrcnt; unsigned long ttl, sig_ttl; - struct blockdata *key; union all_addr a; - int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE | DNSSEC_FAIL_NOKEY; + int failflags = DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE; + char valid_digest[255]; + static unsigned char **cached_digest; + static size_t cached_digest_size = 0; - if (ntohs(header->qdcount) != 1 || - RCODE(header) == SERVFAIL || RCODE(header) == REFUSED || - !extract_name(header, plen, &p, name, 1, 4)) + if (ntohs(header->qdcount) != 1 || RCODE(header) != NOERROR || !extract_name(header, plen, &p, name, 1, 4)) return STAT_BOGUS | DNSSEC_FAIL_NOKEY; GETSHORT(qtype, p); GETSHORT(qclass, p); - if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0) + if (qtype != T_DNSKEY || qclass != class || + !explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) || + rrcnt == 0) return STAT_BOGUS | DNSSEC_FAIL_NOKEY; + if (sigcnt == 0) + return STAT_BOGUS | DNSSEC_FAIL_NOSIG; + /* See if we have cached a DS record which validates this key */ if (!(crecp = cache_find_by_name(NULL, name, now, F_DS))) { strcpy(keyname, name); return STAT_NEED_DS; } - + /* NOTE, we need to find ONE DNSKEY which matches the DS */ - for (valid = 0, j = ntohs(header->ancount); j != 0 && !valid; j--) + for (j = ntohs(header->ancount); j != 0; j--) { /* Ensure we have type, class TTL and length */ if (!(rc = extract_name(header, plen, &p, name, 0, 10))) @@ -799,7 +760,7 @@ GETLONG(ttl, p); GETSHORT(rdlen, p); - if (!CHECK_LEN(header, p, plen, rdlen) || rdlen < 4) + if (!CHECK_LEN(header, p, plen, rdlen)) return STAT_BOGUS; /* bad packet */ if (qclass != class || qtype != T_DNSKEY || rc == 2) @@ -807,173 +768,204 @@ p += rdlen; continue; } - + + if (rdlen < 5) + return STAT_BOGUS; /* min 1 byte key! */ + psave = p; GETSHORT(flags, p); if (*p++ != 3) - return STAT_BOGUS | DNSSEC_FAIL_NOKEY; - algo = *p++; - keytag = dnskey_keytag(algo, flags, p, rdlen - 4); - key = NULL; - - /* key must have zone key flag set */ - if (flags & 0x100) { - key = blockdata_alloc((char*)p, rdlen - 4); - failflags &= ~DNSSEC_FAIL_NOZONE; + p = psave + rdlen; + continue; } + algo = *p++; + keyaddr = p; + keytag = dnskey_keytag(algo, flags, keyaddr, rdlen - 4); - p = psave; - - if (!ADD_RDLEN(header, p, plen, rdlen)) - { - if (key) - blockdata_free(key); - return STAT_BOGUS; /* bad packet */ - } + p = psave + rdlen; - /* No zone key flag or malloc failure */ - if (!key) + /* key must have zone key flag set */ + if (!(flags & 0x100)) continue; + failflags &= ~DNSSEC_FAIL_NOZONE; + + /* clear digest cache. */ + memset(valid_digest, 0, sizeof(valid_digest)); + for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS)) { void *ctx; unsigned char *digest, *ds_digest; const struct nettle_hash *hash; - int sigcnt, rrcnt; int wire_len; - if (recp1->addr.ds.algo == algo && - recp1->addr.ds.keytag == keytag && - recp1->uid == (unsigned int)class) - { - failflags &= ~DNSSEC_FAIL_NOKEY; + if ((recp1->flags & F_NEG) || + recp1->addr.ds.algo != algo || + recp1->addr.ds.keytag != keytag || + recp1->uid != (unsigned int)class) + continue; + + if (!(hash = hash_find(ds_digest_name(recp1->addr.ds.digest)))) + continue; + + failflags &= ~DNSSEC_FAIL_NODSSUP; - if (!(hash = hash_find(ds_digest_name(recp1->addr.ds.digest)))) - continue; - else - failflags &= ~DNSSEC_FAIL_NODSSUP; + if (recp1->addr.ds.keylen != (int)hash->digest_size || + !(ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL))) + continue; + if (valid_digest[recp1->addr.ds.digest]) + digest = cached_digest[recp1->addr.ds.digest]; + else + { + /* computing a hash is a unit of crypto work. */ + if (dec_counter(validate_counter, NULL)) + return STAT_ABANDONED; + if (!hash_init(hash, &ctx, &digest)) continue; wire_len = to_wire(name); /* Note that digest may be different between DSs, so - we can't move this outside the loop. */ + we can't move this outside the loop. We keep + copies of each digest we make for this key, + so maximum digest work is O(keys x digests_types) + rather then O(keys x DSs) */ hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name); hash->update(ctx, (unsigned int)rdlen, psave); hash->digest(ctx, hash->digest_size, digest); from_wire(name); - - if (!(recp1->flags & F_NEG) && - recp1->addr.ds.keylen == (int)hash->digest_size && - (ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)) && - memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 && - explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) && - rrcnt != 0) - { - if (sigcnt == 0) - continue; - else - failflags &= ~DNSSEC_FAIL_NOSIG; - - rc = validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname, - NULL, key, rdlen - 4, algo, keytag, &sig_ttl); - failflags &= rc; - - if (STAT_ISEQUAL(rc, STAT_SECURE)) + if (recp1->addr.ds.digest >= cached_digest_size) + { + unsigned char **new; + + /* whine_malloc zeros memory */ + if ((new = whine_malloc((recp1->addr.ds.digest + 5) * sizeof(unsigned char *)))) + { + if (cached_digest_size != 0) + { + memcpy(new, cached_digest, cached_digest_size * sizeof(unsigned char *)); + free(cached_digest); + } + + cached_digest_size = recp1->addr.ds.digest + 5; + cached_digest = new; + } + } + + if (recp1->addr.ds.digest < cached_digest_size) + { + if (!cached_digest[recp1->addr.ds.digest]) + cached_digest[recp1->addr.ds.digest] = whine_malloc(recp1->addr.ds.keylen); + + if (cached_digest[recp1->addr.ds.digest]) { - valid = 1; - break; + memcpy(cached_digest[recp1->addr.ds.digest], digest, recp1->addr.ds.keylen); + valid_digest[recp1->addr.ds.digest] = 1; } } } - } - blockdata_free(key); - } - - if (valid) - { - /* DNSKEY RRset determined to be OK, now cache it. */ - cache_start_insert(); - - p = skip_questions(header, plen); - - for (j = ntohs(header->ancount); j != 0; j--) - { - /* Ensure we have type, class TTL and length */ - if (!(rc = extract_name(header, plen, &p, name, 0, 10))) - return STAT_BOGUS; /* bad packet */ - - GETSHORT(qtype, p); - GETSHORT(qclass, p); - GETLONG(ttl, p); - GETSHORT(rdlen, p); - - /* TTL may be limited by sig. */ - if (sig_ttl < ttl) - ttl = sig_ttl; - - if (!CHECK_LEN(header, p, plen, rdlen)) - return STAT_BOGUS; /* bad packet */ - if (qclass == class && rc == 1) + if (memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0) { - psave = p; + /* Found the key validated by a DS record. + Now check the self-sig for the entire key RRset using that key. + Note that validate_rrset() will never return STAT_NEED_KEY here, + since we supply the key it will use as an argument. */ + struct blockdata *key; + + if (!(key = blockdata_alloc((char *)keyaddr, rdlen - 4))) + break; + + rc = validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname, + NULL, key, rdlen - 4, algo, keytag, &sig_ttl, validate_counter); + + blockdata_free(key); + + if (STAT_ISEQUAL(rc, STAT_ABANDONED)) + return rc; + + /* can't validate KEY RRset with this key, see if there's another that + will, which is validated by another DS. */ + if (!STAT_ISEQUAL(rc, STAT_SECURE)) + break; + + /* DNSKEY RRset determined to be OK, now cache it. */ + cache_start_insert(); - if (qtype == T_DNSKEY) + p = skip_questions(header, plen); + + for (j = ntohs(header->ancount); j != 0; j--) { - if (rdlen < 4) + /* Ensure we have type, class TTL and length */ + if (!(rc = extract_name(header, plen, &p, name, 0, 10))) return STAT_BOGUS; /* bad packet */ - GETSHORT(flags, p); - if (*p++ != 3) - return STAT_BOGUS; - algo = *p++; - keytag = dnskey_keytag(algo, flags, p, rdlen - 4); + GETSHORT(qtype, p); + GETSHORT(qclass, p); + GETLONG(ttl, p); + GETSHORT(rdlen, p); - if ((key = blockdata_alloc((char*)p, rdlen - 4))) - { - a.key.keylen = rdlen - 4; - a.key.keydata = key; - a.key.algo = algo; - a.key.keytag = keytag; - a.key.flags = flags; - - if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK)) - { - blockdata_free(key); - return STAT_BOGUS; - } - else - { - a.log.keytag = keytag; - a.log.algo = algo; - if (algo_digest_name(algo)) - log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu", 0); - else - log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)", 0); - } - } + /* TTL may be limited by sig. */ + if (sig_ttl < ttl) + ttl = sig_ttl; + + if (!CHECK_LEN(header, p, plen, rdlen)) + return STAT_BOGUS; /* bad packet */ + + psave = p; + + if (qclass == class && rc == 1 && qtype == T_DNSKEY) + { + if (rdlen < 4) + return STAT_BOGUS; /* min 1 byte key! */ + + GETSHORT(flags, p); + if (*p++ == 3) + { + algo = *p++; + keytag = dnskey_keytag(algo, flags, p, rdlen - 4); + + if (!(key = blockdata_alloc((char*)p, rdlen - 4))) + return STAT_BOGUS; + + a.key.keylen = rdlen - 4; + a.key.keydata = key; + a.key.algo = algo; + a.key.keytag = keytag; + a.key.flags = flags; + + if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DNSKEY | F_DNSSECOK)) + { + blockdata_free(key); + return STAT_BOGUS; + } + + a.log.keytag = keytag; + a.log.algo = algo; + if (algo_digest_name(algo)) + log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu", 0); + else + log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)", 0); + } + } + + p = psave + rdlen; } - - p = psave; + + /* commit cache insert. */ + cache_end_insert(); + return STAT_OK; } - - if (!ADD_RDLEN(header, p, plen, rdlen)) - return STAT_BOGUS; /* bad packet */ } - - /* commit cache insert. */ - cache_end_insert(); - return STAT_OK; } - + log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY", 0); return STAT_BOGUS | failflags; } @@ -991,12 +983,14 @@ STAT_BOGUS no DS in reply or not signed, fails validation, bad packet. STAT_NEED_KEY DNSKEY records to validate a DS not found, name in keyname STAT_NEED_DS DS record needed. + STAT_ABANDONED resource exhaustion. */ -int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class) +int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, + char *keyname, int class, int *validate_counter) { unsigned char *p = (unsigned char *)(header+1); - int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0, found_supported = 0; + int qtype, qclass, rc, i, neganswer = 0, nons = 0, servfail = 0, neg_ttl = 0, found_supported = 0; int aclass, atype, rdlen, flags; unsigned long ttl; union all_addr a; @@ -1009,41 +1003,51 @@ GETSHORT(qclass, p); if (qtype != T_DS || qclass != class) - rc = STAT_BOGUS; + return STAT_BOGUS; + + /* A SERVFAIL answer has been seen to a DS query not at start of authority, + so treat it as such and continue to search for a DS or proof of no existence + further down the tree. */ + if (RCODE(header) == SERVFAIL) + servfail = neganswer = nons = 1; else - rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl); - - if (STAT_ISEQUAL(rc, STAT_INSECURE)) { - my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name); - log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0); - return STAT_BOGUS | DNSSEC_FAIL_INDET; - } + rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl, validate_counter); - p = (unsigned char *)(header+1); - if (!extract_name(header, plen, &p, name, 1, 4)) - return STAT_BOGUS; + if (STAT_ISEQUAL(rc, STAT_INSECURE)) + { + my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name); + log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0); + return STAT_BOGUS | DNSSEC_FAIL_INDET; + } + + p = (unsigned char *)(header+1); + if (!extract_name(header, plen, &p, name, 1, 4)) + return STAT_BOGUS; - p += 4; /* qtype, qclass */ + p += 4; /* qtype, qclass */ + + /* If the key needed to validate the DS is on the same domain as the DS, we'll + loop getting nowhere. Stop that now. This can happen of the DS answer comes + from the DS's zone, and not the parent zone. */ + if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname)) + { + log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0); + return STAT_BOGUS; + } - /* If the key needed to validate the DS is on the same domain as the DS, we'll - loop getting nowhere. Stop that now. This can happen of the DS answer comes - from the DS's zone, and not the parent zone. */ - if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname)) - { - log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0); - return STAT_BOGUS; + if (!STAT_ISEQUAL(rc, STAT_SECURE)) + return rc; } - if (!STAT_ISEQUAL(rc, STAT_SECURE)) - return rc; - if (!neganswer) { cache_start_insert(); for (i = 0; i < ntohs(header->ancount); i++) { + unsigned char *psave; + if (!(rc = extract_name(header, plen, &p, name, 0, 10))) return STAT_BOGUS; /* bad packet */ @@ -1054,15 +1058,16 @@ if (!CHECK_LEN(header, p, plen, rdlen)) return STAT_BOGUS; /* bad packet */ + + psave = p; if (aclass == class && atype == T_DS && rc == 1) { int algo, digest, keytag; - unsigned char *psave = p; struct blockdata *key; - if (rdlen < 4) - return STAT_BOGUS; /* bad packet */ + if (rdlen < 5) + return STAT_BOGUS; /* min 1 byte digest! */ GETSHORT(keytag, p); algo = *p++; @@ -1073,7 +1078,7 @@ a.log.keytag = keytag; a.log.algo = algo; a.log.digest = digest; - log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0); + log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS for keytag %hu, algo %hu, digest %hu (not supported)", 0); neg_ttl = ttl; } else if ((key = blockdata_alloc((char*)p, rdlen - 4))) @@ -1094,16 +1099,13 @@ a.log.keytag = keytag; a.log.algo = algo; a.log.digest = digest; - log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0); + log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS for keytag %hu, algo %hu, digest %hu", 0); found_supported = 1; } } - - p = psave; } - - if (!ADD_RDLEN(header, p, plen, rdlen)) - return STAT_BOGUS; /* bad packet */ + + p = psave + rdlen; } cache_end_insert(); @@ -1135,7 +1137,8 @@ cache_end_insert(); if (neganswer) - log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0); + log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, + servfail ? "SERVFAIL" : (nons ? "no DS/cut" : "no DS"), 0); return STAT_OK; } @@ -1205,6 +1208,7 @@ } } +/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, unsigned char **labels, int nsec_count, char *workspace1_in, char *workspace2, char *name, int type, int *nons) { @@ -1224,12 +1228,12 @@ p = nsecs[i]; if (!extract_name(header, plen, &p, workspace1, 1, 10)) - return 0; + return DNSSEC_FAIL_BADPACKET; p += 8; /* class, type, TTL */ GETSHORT(rdlen, p); psave = p; - if (!extract_name(header, plen, &p, workspace2, 1, 10)) - return 0; + if (!extract_name(header, plen, &p, workspace2, 1, 0)) + return DNSSEC_FAIL_BADPACKET; /* If NSEC comes from wildcard expansion, use original wildcard as name for computation. */ @@ -1257,12 +1261,13 @@ { /* 4035 para 5.4. Last sentence */ if (type == T_NSEC || type == T_RRSIG) - return 1; + return 0; /* NSEC with the same name as the RR we're testing, check that the type in question doesn't appear in the type map */ rdlen -= p - psave; - /* rdlen is now length of type map, and p points to it */ + /* rdlen is now length of type map, and p points to it + packet checked to be as long as rdlen implies in prove_non_existence() */ /* If we can prove that there's no NS record, return that information. */ if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0) @@ -1273,25 +1278,25 @@ /* A CNAME answer would also be valid, so if there's a CNAME is should have been returned. */ if ((p[2] & (0x80 >> T_CNAME)) != 0) - return 0; + return DNSSEC_FAIL_NONSEC; /* If the SOA bit is set for a DS record, then we have the DS from the wrong side of the delegation. For the root DS, this is expected. */ if (name_labels != 0 && type == T_DS && (p[2] & (0x80 >> T_SOA)) != 0) - return 0; + return DNSSEC_FAIL_NONSEC; } while (rdlen >= 2) { if (!CHECK_LEN(header, p, plen, rdlen)) - return 0; + return DNSSEC_FAIL_BADPACKET; if (p[0] == type >> 8) { /* Does the NSEC say our type exists? */ if (offset < p[1] && (p[offset+2] & mask) != 0) - return 0; + return DNSSEC_FAIL_NONSEC; break; /* finished checking */ } @@ -1300,24 +1305,24 @@ p += p[1]; } - return 1; + return 0; } else if (rc == -1) { /* Normal case, name falls between NSEC name and next domain name, wrap around case, name falls between NSEC name (rc == -1) and end */ if (hostname_cmp(workspace2, name) >= 0 || hostname_cmp(workspace1, workspace2) >= 0) - return 1; + return 0; } else { /* wrap around case, name falls between start and next domain name */ if (hostname_cmp(workspace1, workspace2) >= 0 && hostname_cmp(workspace2, name) >=0 ) - return 1; + return 0; } } - return 0; + return DNSSEC_FAIL_NONSEC; } /* return digest length, or zero on error */ @@ -1391,23 +1396,23 @@ for (i = 0; i < nsec_count; i++) if ((p = nsecs[i])) { - if (!extract_name(header, plen, &p, workspace1, 1, 0) || + if (!extract_name(header, plen, &p, workspace1, 1, 10) || !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2))) return 0; p += 8; /* class, type, TTL */ GETSHORT(rdlen, p); + psave = p; + + /* packet checked to be as long as implied by rdlen, salt_len and hash_len in prove_non_existence() */ p++; /* algo */ flags = *p++; /* flags */ p += 2; /* iterations */ salt_len = *p++; /* salt_len */ p += salt_len; /* salt */ hash_len = *p++; /* p now points to next hashed name */ - - if (!CHECK_LEN(header, p, plen, hash_len)) - return 0; - + if (digest_len == base32_len && hash_len == base32_len) { int rc = memcmp(workspace2, digest, digest_len); @@ -1415,7 +1420,8 @@ if (rc == 0) { /* We found an NSEC3 whose hashed name exactly matches the query, so - we just need to check the type map. p points to the RR data for the record. */ + we just need to check the type map. p points to the RR data for the record. + Note we have packet length up to rdlen bytes checked. */ int offset = (type & 0xff) >> 3; int mask = 0x80 >> (type & 0x07); @@ -1423,15 +1429,12 @@ p += hash_len; /* skip next-domain hash */ rdlen -= p - psave; - if (!CHECK_LEN(header, p, plen, rdlen)) - return 0; - if (rdlen >= 2 && p[0] == 0) { /* If we can prove that there's no NS record, return that information. */ if (nons && (p[2] & (0x80 >> T_NS)) != 0) *nons = 0; - + /* A CNAME answer would also be valid, so if there's a CNAME is should have been returned. */ if ((p[2] & (0x80 >> T_CNAME)) != 0) @@ -1490,8 +1493,9 @@ return 0; } -static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count, - char *workspace1, char *workspace2, char *name, int type, char *wildname, int *nons) +/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */ +static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count, char *workspace1, + char *workspace2, char *name, int type, char *wildname, int *nons, int *validate_counter) { unsigned char *salt, *p, *digest; int digest_len, i, iterations, salt_len, base32_len, algo = 0; @@ -1511,9 +1515,9 @@ for (i = 0; i < nsec_count; i++) { if (!(p = skip_name(nsecs[i], header, plen, 15))) - return 0; /* bad packet */ + return DNSSEC_FAIL_BADPACKET; /* bad packet */ - p += 10; /* type, class, TTL, rdlen */ + p += 10; /* type, class, TTL, rdlen */ algo = *p++; if ((hash = hash_find(nsec3_digest_name(algo)))) @@ -1522,23 +1526,18 @@ /* No usable NSEC3s */ if (i == nsec_count) - return 0; + return DNSSEC_FAIL_NONSEC; p++; /* flags */ GETSHORT (iterations, p); - /* Upper-bound iterations, to avoid DoS. - Strictly, there are lower bounds for small keys, but - since we don't have key size info here, at least limit - to the largest bound, for 4096-bit keys. RFC 5155 10.3 */ - if (iterations > 2500) - return 0; + /* Upper-bound iterations, to avoid DoS. RFC 9276 refers. */ + if (iterations > daemon->limit[LIMIT_NSEC3_ITERS]) + return DNSSEC_FAIL_NSEC3_ITERS; salt_len = *p++; salt = p; - if (!CHECK_LEN(header, salt, plen, salt_len)) - return 0; /* bad packet */ - + /* Now prune so we only have NSEC3 records with same iterations, salt and algo */ for (i = 0; i < nsec_count; i++) { @@ -1568,9 +1567,6 @@ if (salt_len != *p++) continue; - if (!CHECK_LEN(header, p, plen, salt_len)) - return 0; /* bad packet */ - if (memcmp(p, salt, salt_len) != 0) continue; @@ -1578,11 +1574,14 @@ nsecs[i] = nsec3p; } + if (dec_counter(validate_counter, NULL)) + return DNSSEC_FAIL_WORK; + if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0) - return 0; + return DNSSEC_FAIL_NONSEC; if (check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, nons, count_labels(name))) - return 1; + return 0; /* Can't find an NSEC3 which covers the name directly, we need the "closest encloser NSEC3" or an answer inferred from a wildcard record. */ @@ -1597,15 +1596,20 @@ if (wildname && hostname_isequal(closest_encloser, wildname)) break; + if (dec_counter(validate_counter, NULL)) + return DNSSEC_FAIL_WORK; + if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0) - return 0; + return DNSSEC_FAIL_NONSEC; for (i = 0; i < nsec_count; i++) if ((p = nsecs[i])) { - if (!extract_name(header, plen, &p, workspace1, 1, 0) || - !(base32_len = base32_decode(workspace1, (unsigned char *)workspace2))) - return 0; + if (!extract_name(header, plen, &p, workspace1, 1, 0)) + return DNSSEC_FAIL_BADPACKET; + + if (!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2))) + return DNSSEC_FAIL_NONSEC; if (digest_len == base32_len && memcmp(digest, workspace2, digest_len) == 0) @@ -1620,14 +1624,17 @@ while ((closest_encloser = strchr(closest_encloser, '.'))); if (!closest_encloser || !next_closest) - return 0; + return DNSSEC_FAIL_NONSEC; /* Look for NSEC3 that proves the non-existence of the next-closest encloser */ + if (dec_counter(validate_counter, NULL)) + return DNSSEC_FAIL_WORK; + if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0) - return 0; + return DNSSEC_FAIL_NONSEC; if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1)) - return 0; + return DNSSEC_FAIL_NONSEC; /* Finally, check that there's no seat of wildcard synthesis */ if (!wildname) @@ -1638,17 +1645,22 @@ wildcard--; *wildcard = '*'; + if (dec_counter(validate_counter, NULL)) + return DNSSEC_FAIL_WORK; + if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0) - return 0; + return DNSSEC_FAIL_NONSEC; if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1)) - return 0; + return DNSSEC_FAIL_NONSEC; } - return 1; + return 0; } -static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass, char *wildname, int *nons, int *nsec_ttl) +/* returns 0 on success, or DNSSEC_FAIL_* value on failure. */ +static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass, + char *wildname, int *nons, int *nsec_ttl, int *validate_counter) { static unsigned char **nsecset = NULL, **rrsig_labels = NULL; static int nsecset_sz = 0, rrsig_labels_sz = 0; @@ -1660,7 +1672,7 @@ /* Move to NS section */ if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen))) - return 0; + return DNSSEC_FAIL_BADPACKET; auth_start = p; @@ -1669,13 +1681,16 @@ unsigned char *pstart = p; if (!extract_name(header, plen, &p, daemon->workspacename, 1, 10)) - return 0; + return DNSSEC_FAIL_BADPACKET; GETSHORT(type, p); GETSHORT(class, p); GETLONG(ttl, p); GETSHORT(rdlen, p); - + + if (!CHECK_LEN(header, p, plen, rdlen)) + return DNSSEC_FAIL_BADPACKET; + if (class == qclass && (type == T_NSEC || type == T_NSEC3)) { if (nsec_ttl) @@ -1688,12 +1703,12 @@ /* No mixed NSECing 'round here, thankyouverymuch */ if (type_found != 0 && type_found != type) - return 0; + return DNSSEC_FAIL_NONSEC; type_found = type; if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found)) - return 0; + return DNSSEC_FAIL_BADPACKET; if (type == T_NSEC) { @@ -1708,30 +1723,33 @@ int res, j, rdlen1, type1, class1; if (!expand_workspace(&rrsig_labels, &rrsig_labels_sz, nsecs_found)) - return 0; + return DNSSEC_FAIL_BADPACKET; rrsig_labels[nsecs_found] = NULL; for (j = ntohs(header->nscount); j != 0; j--) { - if (!(res = extract_name(header, plen, &p1, daemon->workspacename, 0, 10))) - return 0; + unsigned char *psav; + if (!(res = extract_name(header, plen, &p1, daemon->workspacename, 0, 10))) + return DNSSEC_FAIL_BADPACKET; + GETSHORT(type1, p1); GETSHORT(class1, p1); p1 += 4; /* TTL */ GETSHORT(rdlen1, p1); + psav = p1; + if (!CHECK_LEN(header, p1, plen, rdlen1)) - return 0; + return DNSSEC_FAIL_BADPACKET; if (res == 1 && class1 == qclass && type1 == T_RRSIG) { int type_covered; - unsigned char *psav = p1; - + if (rdlen1 < 18) - return 0; /* bad packet */ + return DNSSEC_FAIL_BADPACKET; /* bad packet */ GETSHORT(type_covered, p1); @@ -1743,33 +1761,56 @@ if (!rrsig_labels[nsecs_found]) rrsig_labels[nsecs_found] = p1; else if (*rrsig_labels[nsecs_found] != *p1) /* algo */ - return 0; - } - p1 = psav; + return DNSSEC_FAIL_NONSEC; + } } - if (!ADD_RDLEN(header, p1, plen, rdlen1)) - return 0; + p1 = psav + rdlen1; } /* Must have found at least one sig. */ if (!rrsig_labels[nsecs_found]) - return 0; + return DNSSEC_FAIL_NONSEC; + } + else if (type == T_NSEC3) + { + /* Decode the packet structure enough to check that rdlen is big enough + to contain everything other than the type bitmap. + (packet checked to be long enough to contain rdlen above) + We don't need to do any further length checks in check_nes3_coverage() + or prove_non_existence_nsec3() */ + + int salt_len, hash_len; + unsigned char *psav = p; + + if (rdlen < 5) + return DNSSEC_FAIL_BADPACKET; + + p += 4; /* algo, flags, iterations */ + salt_len = *p++; /* salt_len */ + if (rdlen < (6 + salt_len)) + return DNSSEC_FAIL_BADPACKET; /* check up to hash_length */ + + p += salt_len; /* salt */ + hash_len = *p++; + if (rdlen < (6 + salt_len + hash_len)) + return DNSSEC_FAIL_BADPACKET; /* check to end of next hashed name */ + + p = psav; } nsecset[nsecs_found++] = pstart; } - if (!ADD_RDLEN(header, p, plen, rdlen)) - return 0; + p += rdlen; } if (type_found == T_NSEC) return prove_non_existence_nsec(header, plen, nsecset, rrsig_labels, nsecs_found, daemon->workspacename, keyname, name, qtype, nons); else if (type_found == T_NSEC3) - return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons); + return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons, validate_counter); else - return 0; + return DNSSEC_FAIL_NONSEC; } /* Check signing status of name. @@ -1864,16 +1905,17 @@ STAT_BOGUS signature is wrong, bad packet, no validation where there should be. STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname, class in *class) STAT_NEED_DS need DS to complete validation (name is returned in keyname) + STAT_ABANDONED resource exhaustion. daemon->rr_status points to a char array which corressponds to the RRs in the answer and auth sections. This is set to >1 for each RR which is validated, and 0 for any which aren't. When validating replies to DS records, we're only interested in the NSEC{3} RRs in the auth section. Other RRs in that section missing sigs will not cause am INSECURE reply. We determine this mode - is the nons argument is non-NULL. + if the nons argument is non-NULL. */ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, - int *class, int check_unsigned, int *neganswer, int *nons, int *nsec_ttl) + int *class, int check_unsigned, int *neganswer, int *nons, int *nsec_ttl, int *validate_counter) { static unsigned char **targets = NULL; static int target_sz = 0; @@ -1882,7 +1924,7 @@ int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx; int i, j, rc = STAT_INSECURE; int secure = STAT_SECURE; - + int rc_nsec; /* extend rr_status if necessary */ if (daemon->rr_status_sz < ntohs(header->ancount) + ntohs(header->nscount)) { @@ -2048,9 +2090,9 @@ { unsigned long sig_ttl; rc = validate_rrset(now, header, plen, class1, type1, sigcnt, - rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl); + rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl, validate_counter); - if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS)) + if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS) || STAT_ISEQUAL(rc, STAT_ABANDONED)) { if (class) *class = class1; /* Class for DS or DNSKEY */ @@ -2084,8 +2126,8 @@ That's not a problem since if the RRsets later fail we'll return BOGUS then. */ if (STAT_ISEQUAL(rc, STAT_SECURE_WILDCARD) && - !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL)) - return STAT_BOGUS | DNSSEC_FAIL_NONSEC; + ((rc_nsec = prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL, validate_counter))) != 0) + return (rc_nsec & DNSSEC_FAIL_WORK) ? STAT_ABANDONED : (STAT_BOGUS | rc_nsec); rc = STAT_SECURE; } @@ -2110,20 +2152,24 @@ /* For anything other than a DS record, this situation is OK if either the answer is in an unsigned zone, or there's a NSEC records. */ - if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl)) + if ((rc_nsec = prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl, validate_counter)) != 0) { + if (rc_nsec & DNSSEC_FAIL_WORK) + return STAT_ABANDONED; + /* Empty DS without NSECS */ if (qtype == T_DS) - return STAT_BOGUS | DNSSEC_FAIL_NONSEC; + return STAT_BOGUS | rc_nsec; - if (!STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE)) + if ((rc_nsec & (DNSSEC_FAIL_NONSEC | DNSSEC_FAIL_NSEC3_ITERS)) && + !STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE)) { if (class) *class = qclass; /* Class for NEED_DS or NEED_KEY */ return rc; } - return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */ + return STAT_BOGUS | rc_nsec; /* signed zone, no NSECs */ } } @@ -2205,6 +2251,8 @@ return EDE_NO_DNSKEY; else if (status & DNSSEC_FAIL_NODSSUP) return EDE_USUPDS; + else if (status & DNSSEC_FAIL_NSEC3_ITERS) + return EDE_UNS_NS3_ITER; else if (status & DNSSEC_FAIL_NONSEC) return EDE_NO_NSEC; else if (status & DNSSEC_FAIL_INDET) diff -Nru dnsmasq-2.89/src/domain-match.c dnsmasq-2.90/src/domain-match.c --- dnsmasq-2.89/src/domain-match.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/domain-match.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -253,9 +253,10 @@ if (highout) *highout = nhigh; - if (nlow == nhigh) + /* qlen == -1 when we failed to match even an empty query, if there are no default servers. */ + if (nlow == nhigh || qlen == -1) return 0; - + return 1; } diff -Nru dnsmasq-2.89/src/domain.c dnsmasq-2.90/src/domain.c --- dnsmasq-2.89/src/domain.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/domain.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,12 +22,13 @@ static struct cond_domain *search_domain6(struct in6_addr *addr, struct cond_domain *c); static int match_domain6(struct in6_addr *addr, struct cond_domain *c); -int is_name_synthetic(int flags, char *name, union all_addr *addr) +int is_name_synthetic(int flags, char *name, union all_addr *addrp) { char *p; struct cond_domain *c = NULL; int prot = (flags & F_IPV6) ? AF_INET6 : AF_INET; - + union all_addr addr; + for (c = daemon->synth_domains; c; c = c->next) { int found = 0; @@ -74,7 +75,7 @@ if (!c->is6 && index <= ntohl(c->end.s_addr) - ntohl(c->start.s_addr)) { - addr->addr4.s_addr = htonl(ntohl(c->start.s_addr) + index); + addr.addr4.s_addr = htonl(ntohl(c->start.s_addr) + index); found = 1; } } @@ -86,8 +87,8 @@ index <= addr6part(&c->end6) - addr6part(&c->start6)) { u64 start = addr6part(&c->start6); - addr->addr6 = c->start6; - setaddr6part(&addr->addr6, start + index); + addr.addr6 = c->start6; + setaddr6part(&addr.addr6, start + index); found = 1; } } @@ -135,8 +136,8 @@ } } - if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr)) - found = (prot == AF_INET) ? match_domain(addr->addr4, c) : match_domain6(&addr->addr6, c); + if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, &addr)) + found = (prot == AF_INET) ? match_domain(addr.addr4, c) : match_domain6(&addr.addr6, c); } /* restore name */ @@ -148,7 +149,12 @@ if (found) - return 1; + { + if (addrp) + *addrp = addr; + + return 1; + } } return 0; diff -Nru dnsmasq-2.89/src/dump.c dnsmasq-2.90/src/dump.c --- dnsmasq-2.89/src/dump.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/dump.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/edns0.c dnsmasq-2.90/src/edns0.c --- dnsmasq-2.89/src/edns0.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/edns0.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -178,7 +178,7 @@ memcpy(buff, datap, rdlen); /* now, delete OPT RR */ - plen = rrfilter(header, plen, RRFILTER_EDNS0); + rrfilter(header, &plen, RRFILTER_EDNS0); /* Now, force addition of a new one */ p = NULL; @@ -191,16 +191,13 @@ if (!(p = skip_questions(header, plen)) || !(p = skip_section(p, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), - header, plen))) - { - free(buff); - return plen; - } - if (p + 11 > limit) - { - free(buff); - return plen; /* Too big */ - } + header, plen)) || + p + 11 > limit) + { + free(buff); + return plen; /* bad packet */ + } + *p++ = 0; /* empty name */ PUTSHORT(T_OPT, p); PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */ diff -Nru dnsmasq-2.89/src/forward.c dnsmasq-2.90/src/forward.c --- dnsmasq-2.89/src/forward.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/forward.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -338,7 +338,8 @@ if (ad_reqd) forward->flags |= FREC_AD_QUESTION; #ifdef HAVE_DNSSEC - forward->work_counter = DNSSEC_WORK; + forward->work_counter = daemon->limit[LIMIT_WORK]; + forward->validate_counter = daemon->limit[LIMIT_CRYPTO]; if (do_bit) forward->flags |= FREC_DO_QUESTION; #endif @@ -687,14 +688,13 @@ { unsigned char *pheader, *sizep; struct ipsets *ipsets = NULL, *nftsets = NULL; - int munged = 0, is_sign; + int is_sign; unsigned int rcode = RCODE(header); size_t plen; (void)ad_reqd; (void)do_bit; - (void)bogusanswer; - + #ifdef HAVE_IPSET if (daemon->ipsets && extract_request(header, n, daemon->namebuff, NULL)) ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff); @@ -721,7 +721,7 @@ if (added_pheader) { /* client didn't send EDNS0, we added one, strip it off before returning answer. */ - n = rrfilter(header, n, RRFILTER_EDNS0); + rrfilter(header, &n, RRFILTER_EDNS0); pheader = NULL; } else @@ -782,101 +782,99 @@ server->flags |= SERV_WARNED_RECURSIVE; } - if (daemon->bogus_addr && rcode != NXDOMAIN && - check_for_bogus_wildcard(header, n, daemon->namebuff, now)) + if (header->hb3 & HB3_TC) { - munged = 1; - SET_RCODE(header, NXDOMAIN); - header->hb3 &= ~HB3_AA; - cache_secure = 0; - ede = EDE_BLOCKED; - } - else - { - int doctored = 0; - - if (rcode == NXDOMAIN && - extract_request(header, n, daemon->namebuff, NULL)) - { - if (check_for_local_domain(daemon->namebuff, now) || - lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL)) - { - /* if we forwarded a query for a locally known name (because it was for - an unknown type) and the answer is NXDOMAIN, convert that to NODATA, - since we know that the domain exists, even if upstream doesn't */ - munged = 1; - header->hb3 |= HB3_AA; - SET_RCODE(header, NOERROR); - cache_secure = 0; - } - } + log_query(F_UPSTREAM, NULL, NULL, "truncated", 0); + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); + } - /* Before extract_addresses() */ - if (rcode == NOERROR) + if (!(header->hb3 & HB3_TC) && (!bogusanswer || (header->hb4 & HB4_CD))) + { + if (rcode == NXDOMAIN && extract_request(header, n, daemon->namebuff, NULL) && + (check_for_local_domain(daemon->namebuff, now) || lookup_domain(daemon->namebuff, F_CONFIG, NULL, NULL))) { - if (option_bool(OPT_FILTER_A)) - n = rrfilter(header, n, RRFILTER_A); - - if (option_bool(OPT_FILTER_AAAA)) - n = rrfilter(header, n, RRFILTER_AAAA); + /* if we forwarded a query for a locally known name (because it was for + an unknown type) and the answer is NXDOMAIN, convert that to NODATA, + since we know that the domain exists, even if upstream doesn't */ + header->hb3 |= HB3_AA; + SET_RCODE(header, NOERROR); + cache_secure = 0; } - - switch (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored)) + + if (daemon->doctors && do_doctor(header, n, daemon->namebuff)) + cache_secure = 0; + + /* check_for_bogus_wildcard() does it's own caching, so + don't call extract_addresses() if it triggers. */ + if (daemon->bogus_addr && rcode != NXDOMAIN && + check_for_bogus_wildcard(header, n, daemon->namebuff, now)) { - case 1: - my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); - munged = 1; + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); + SET_RCODE(header, NXDOMAIN); + header->hb3 &= ~HB3_AA; cache_secure = 0; ede = EDE_BLOCKED; - break; - - /* extract_addresses() found a malformed answer. */ - case 2: - munged = 1; - SET_RCODE(header, SERVFAIL); - cache_secure = 0; - ede = EDE_OTHER; - break; } + else + { + int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure); - if (doctored) - cache_secure = 0; + if (rc != 0) + { + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); + cache_secure = 0; + } + + if (rc == 1) + { + my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); + ede = EDE_BLOCKED; + } + + if (rc == 2) + { + /* extract_addresses() found a malformed answer. */ + SET_RCODE(header, SERVFAIL); + ede = EDE_OTHER; + } + } + + if (RCODE(header) == NOERROR && rrfilter(header, &n, RRFILTER_CONF) > 0) + ede = EDE_FILTERED; } #ifdef HAVE_DNSSEC - if (bogusanswer && !(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG)) - { - /* Bogus reply, turn into SERVFAIL */ - SET_RCODE(header, SERVFAIL); - munged = 1; - } - if (option_bool(OPT_DNSSEC_VALID)) { - header->hb4 &= ~HB4_AD; - - if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure) + if (bogusanswer) + { + if (!(header->hb4 & HB4_CD) && !option_bool(OPT_DNSSEC_DEBUG)) + { + /* Bogus reply, turn into SERVFAIL */ + SET_RCODE(header, SERVFAIL); + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); + ede = EDE_DNSSEC_BOGUS; + } + } + else if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure) header->hb4 |= HB4_AD; /* If the requestor didn't set the DO bit, don't return DNSSEC info. */ if (!do_bit) - n = rrfilter(header, n, RRFILTER_DNSSEC); + rrfilter(header, &n, RRFILTER_DNSSEC); } #endif - - /* do this after extract_addresses. Ensure NODATA reply and remove - nameserver info. */ - if (munged) - { - header->ancount = htons(0); - header->nscount = htons(0); - header->arcount = htons(0); - header->hb3 &= ~HB3_TC; - } - /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide - sections of the packet. Find the new length here and put back pseudoheader - if it was removed. */ + /* the code above can elide sections of the packet. Find the new length here + and put back pseudoheader if it was removed. */ n = resize_packet(header, n, pheader, plen); if (pheader && ede != EDE_UNSET) @@ -895,23 +893,36 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header, ssize_t plen, int status, time_t now) { + struct frec *orig; + int log_resource = 0; + daemon->log_display_id = forward->frec_src.log_id; /* We've had a reply already, which we're validating. Ignore this duplicate */ if (forward->blocking_query) return; - /* Truncated answer can't be validated. - If this is an answer to a DNSSEC-generated query, we still - need to get the client to retry over TCP, so return - an answer with the TC bit set, even if the actual answer fits. - */ - if (header->hb3 & HB3_TC) - status = STAT_TRUNCATED; - /* If all replies to a query are REFUSED, give up. */ if (RCODE(header) == REFUSED) status = STAT_ABANDONED; + else if (header->hb3 & HB3_TC) + { + /* Truncated answer can't be validated. + If this is an answer to a DNSSEC-generated query, we still + need to get the client to retry over TCP, so return + an answer with the TC bit set, even if the actual answer fits. + */ + status = STAT_TRUNCATED; + if (forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) + { + unsigned char *p = (unsigned char *)(header+1); + if (extract_name(header, plen, &p, daemon->namebuff, 0, 4) == 1) + log_query(F_UPSTREAM | F_NOEXTRA, daemon->namebuff, NULL, "truncated", (forward->flags & FREC_DNSKEY_QUERY) ? T_DNSKEY : T_DS); + } + } + + /* Find the original query that started it all.... */ + for (orig = forward; orig->dependent; orig = orig->dependent); /* As soon as anything returns BOGUS, we stop and unwind, to do otherwise would invite infinite loops, since the answers to DNSKEY and DS queries @@ -919,19 +930,17 @@ if (!STAT_ISEQUAL(status, STAT_BOGUS) && !STAT_ISEQUAL(status, STAT_TRUNCATED) && !STAT_ISEQUAL(status, STAT_ABANDONED)) { if (forward->flags & FREC_DNSKEY_QUERY) - status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class); + status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class, &orig->validate_counter); else if (forward->flags & FREC_DS_QUERY) - status = dnssec_validate_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class); + status = dnssec_validate_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class, &orig->validate_counter); else status = dnssec_validate_reply(now, header, plen, daemon->namebuff, daemon->keyname, &forward->class, !option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC), - NULL, NULL, NULL); -#ifdef HAVE_DUMPFILE - if (STAT_ISEQUAL(status, STAT_BOGUS)) - dump_packet_udp((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS, - header, (size_t)plen, &forward->sentto->addr, NULL, -daemon->port); -#endif + NULL, NULL, NULL, &orig->validate_counter); } + + if (STAT_ISEQUAL(status, STAT_ABANDONED)) + log_resource = 1; /* Can't validate, as we're missing key data. Put this answer aside, whilst we get that. */ @@ -975,18 +984,19 @@ return; } } + else if (orig->work_counter-- == 0) + { + my_syslog(LOG_WARNING, _("limit exceeded: per-query subqueries")); + log_resource = 1; + } else { struct server *server; - struct frec *orig; void *hash; size_t nn; int serverind, fd; struct randfd_list *rfds = NULL; - /* Find the original query that started it all.... */ - for (orig = forward; orig->dependent; orig = orig->dependent); - /* Make sure we don't expire and free the orig frec during the allocation of a new one: third arg of get_new_frec() does that. */ if ((serverind = dnssec_server(forward->sentto, daemon->keyname, NULL, NULL)) != -1 && @@ -995,7 +1005,6 @@ daemon->keyname, forward->class, STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz)) && (hash = hash_questions(header, nn, daemon->namebuff)) && - --orig->work_counter != 0 && (fd = allocate_rfd(&rfds, server)) != -1 && (new = get_new_frec(now, server, 1))) { @@ -1061,6 +1070,21 @@ status = STAT_ABANDONED; } + if (log_resource) + { + /* Log the actual validation that made us barf. */ + unsigned char *p = (unsigned char *)(header+1); + if (extract_name(header, plen, &p, daemon->namebuff, 0, 4) == 1) + my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."), + daemon->namebuff[0] ? daemon->namebuff : "."); + } + +#ifdef HAVE_DUMPFILE + if (STAT_ISEQUAL(status, STAT_BOGUS) || STAT_ISEQUAL(status, STAT_ABANDONED)) + dump_packet_udp((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS, + header, (size_t)plen, &forward->sentto->addr, NULL, -daemon->port); +#endif + /* Validated original answer, all done. */ if (!forward->dependent) return_reply(now, forward, header, plen, status); @@ -1069,7 +1093,7 @@ /* validated subsidiary query/queries, (and cached result) pop that and return to the previous query/queries we were working on. */ struct frec *prev, *nxt = forward->dependent; - + free_frec(forward); while ((prev = nxt)) @@ -1300,7 +1324,10 @@ no_cache_dnssec = 0; if (STAT_ISEQUAL(status, STAT_TRUNCATED)) - header->hb3 |= HB3_TC; + { + header->hb3 |= HB3_TC; + log_query(F_SECSTAT, "result", NULL, "TRUNCATED", 0); + } else { char *result, *domain = "result"; @@ -1326,10 +1353,16 @@ if (extract_request(header, n, daemon->namebuff, NULL)) domain = daemon->namebuff; } - + log_query(F_SECSTAT, domain, &a, result, 0); } } + + if ((daemon->limit[LIMIT_CRYPTO] - forward->validate_counter) > (int)daemon->metrics[METRIC_CRYPTO_HWM]) + daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - forward->validate_counter; + + if ((daemon->limit[LIMIT_WORK] - forward->work_counter) > (int)daemon->metrics[METRIC_WORK_HWM]) + daemon->metrics[METRIC_WORK_HWM] = daemon->limit[LIMIT_WORK] - forward->work_counter; #endif if (option_bool(OPT_NO_REBIND)) @@ -1808,27 +1841,38 @@ #endif else { - int stale; + int stale, filtered; int ad_reqd = do_bit; - u16 hb3 = header->hb3, hb4 = header->hb4; int fd = listen->fd; + struct blockdata *saved_question = blockdata_alloc((char *) header, (size_t)n); /* RFC 6840 5.7 */ if (header->hb4 & HB4_AD) ad_reqd = 1; - + m = answer_request(header, ((char *) header) + udp_size, (size_t)n, - dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale); + dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale, &filtered); if (m >= 1) { - if (stale && have_pseudoheader) + if (have_pseudoheader) { - u16 swap = htons(EDE_STALE); - - m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz, - EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + int ede = EDE_UNSET; + + if (filtered) + ede = EDE_FILTERED; + else if (stale) + ede = EDE_STALE; + + if (ede != EDE_UNSET) + { + u16 swap = htons(ede); + + m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz, + EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + } } + #ifdef HAVE_DUMPFILE dump_packet_udp(DUMP_REPLY, daemon->packet, m, NULL, &source_addr, listen->fd); #endif @@ -1843,34 +1887,31 @@ daemon->metrics[METRIC_DNS_STALE_ANSWERED]++; } - if (m == 0 || stale) + if (stale) + { + /* We answered with stale cache data, so forward the query anyway to + refresh that. */ + m = 0; + + /* We've already answered the client, so don't send it the answer + when it comes back. */ + fd = -1; + } + + if (saved_question) { - if (m != 0) + if (m == 0) { - size_t plen; + blockdata_retrieve(saved_question, (size_t)n, header); - /* We answered with stale cache data, so forward the query anyway to - refresh that. Restore the query from the answer packet. */ - pheader = find_pseudoheader(header, (size_t)m, &plen, NULL, NULL, NULL); - - header->hb3 = hb3; - header->hb4 = hb4; - header->ancount = htons(0); - header->nscount = htons(0); - header->arcount = htons(0); - - m = resize_packet(header, m, pheader, plen); - - /* We've already answered the client, so don't send it the answer - when it comes back. */ - fd = -1; + if (forward_query(fd, &source_addr, &dst_addr, if_index, + header, (size_t)n, ((char *) header) + udp_size, now, NULL, ad_reqd, do_bit, 0)) + daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++; + else + daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++; } - if (forward_query(fd, &source_addr, &dst_addr, if_index, - header, (size_t)n, ((char *) header) + udp_size, now, NULL, ad_reqd, do_bit, 0)) - daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++; - else - daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++; + blockdata_free(saved_question); } } } @@ -1898,7 +1939,7 @@ while (1) { - int data_sent = 0; + int data_sent = 0, timedout = 0; struct server *serv; if (firstsendto == -1) @@ -1936,15 +1977,27 @@ serv->tcpfd = -1; continue; } + +#ifdef TCP_SYNCNT + /* TCP connections by default take ages to time out. + At least on Linux, we can reduce that to only two attempts + to get a reply. For DNS, that's more sensible. */ + mark = 2; + setsockopt(serv->tcpfd, IPPROTO_TCP, TCP_SYNCNT, &mark, sizeof(unsigned int)); +#endif #ifdef MSG_FASTOPEN server_send(serv, serv->tcpfd, packet, qsize + sizeof(u16), MSG_FASTOPEN); if (errno == 0) data_sent = 1; + else if (errno == ETIMEDOUT || errno == EHOSTUNREACH) + timedout = 1; #endif - if (!data_sent && connect(serv->tcpfd, &serv->addr.sa, sa_len(&serv->addr)) == -1) + /* If fastopen failed due to lack of reply, then there's no point in + trying again in non-FASTOPEN mode. */ + if (timedout || (!data_sent && connect(serv->tcpfd, &serv->addr.sa, sa_len(&serv->addr)) == -1)) { close(serv->tcpfd); serv->tcpfd = -1; @@ -1991,7 +2044,7 @@ /* Recurse down the key hierarchy */ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n, int class, char *name, char *keyname, struct server *server, - int have_mark, unsigned int mark, int *keycount) + int have_mark, unsigned int mark, int *keycount, int *validatecount) { int first, last, start, new_status; unsigned char *packet = NULL; @@ -2003,20 +2056,34 @@ int log_save; /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */ - if (--(*keycount) == 0) - new_status = STAT_ABANDONED; - else if (STAT_ISEQUAL(status, STAT_NEED_KEY)) - new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class); + if (STAT_ISEQUAL(status, STAT_NEED_KEY)) + new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class, validatecount); else if (STAT_ISEQUAL(status, STAT_NEED_DS)) - new_status = dnssec_validate_ds(now, header, n, name, keyname, class); + new_status = dnssec_validate_ds(now, header, n, name, keyname, class, validatecount); else new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, !option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC), - NULL, NULL, NULL); + NULL, NULL, NULL, validatecount); - if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY)) + if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY) && !STAT_ISEQUAL(new_status, STAT_ABANDONED)) break; - + + if ((*keycount)-- == 0) + { + my_syslog(LOG_WARNING, _("limit exceeded: per-query subqueries")); + new_status = STAT_ABANDONED; + } + + if (STAT_ISEQUAL(new_status, STAT_ABANDONED)) + { + /* Log the actual validation that made us barf. */ + unsigned char *p = (unsigned char *)(header+1); + if (extract_name(header, n, &p, daemon->namebuff, 0, 4) == 1) + my_syslog(LOG_WARNING, _("validation of %s failed: resource limit exceeded."), + daemon->namebuff[0] ? daemon->namebuff : "."); + break; + } + /* Can't validate because we need a key/DS whose name now in keyname. Make query for same, and recurse to validate */ if (!packet) @@ -2030,7 +2097,7 @@ new_status = STAT_ABANDONED; break; } - + m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz); @@ -2045,10 +2112,11 @@ daemon->log_display_id = ++daemon->log_id; log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, keyname, &server->addr, - STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0); - - new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount); - + STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0); + + new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, + have_mark, mark, keycount, validatecount); + daemon->log_display_id = log_save; if (!STAT_ISEQUAL(new_status, STAT_OK)) @@ -2070,7 +2138,7 @@ unsigned char *tcp_request(int confd, time_t now, union mysockaddr *local_addr, struct in_addr netmask, int auth_dns) { - size_t size = 0; + size_t size = 0, saved_size = 0; int norebind; #ifdef HAVE_CONNTRACK int is_single_query = 0, allowed = 1; @@ -2081,6 +2149,7 @@ int checking_disabled, do_bit, added_pheader = 0, have_pseudoheader = 0; int cacheable, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; size_t m; + struct blockdata *saved_question = NULL; unsigned short qtype; unsigned int gotname; /* Max TCP packet + slop + size */ @@ -2098,9 +2167,8 @@ unsigned char *pheader; unsigned int mark = 0; int have_mark = 0; - int first, last, stale, do_stale = 0; + int first, last, filtered, stale, do_stale = 0; unsigned int flags = 0; - u16 hb3, hb4; if (!packet || getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) return packet; @@ -2155,35 +2223,15 @@ { int ede = EDE_UNSET; - if (query_count == TCP_MAX_QUERIES) - return packet; - - if (do_stale) + if (!do_stale) { - size_t plen; - - /* We answered the last query with stale data. Now try and get fresh data. - Restore query from answer. */ - pheader = find_pseudoheader(header, m, &plen, NULL, NULL, NULL); - - header->hb3 = hb3; - header->hb4 = hb4; - header->ancount = htons(0); - header->nscount = htons(0); - header->arcount = htons(0); + if (query_count == TCP_MAX_QUERIES) + break; - size = resize_packet(header, m, pheader, plen); - } - else - { if (!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) || !(size = c1 << 8 | c2) || !read_write(confd, payload, size, 1)) - return packet; - - /* for stale-answer processing. */ - hb3 = header->hb3; - hb4 = header->hb4; + break; } if (size < (int)sizeof(struct dns_header)) @@ -2290,18 +2338,28 @@ if (do_stale) m = 0; else - /* m > 0 if answered from cache */ - m = answer_request(header, ((char *) header) + 65536, (size_t)size, - dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale); - + { + if (saved_question) + blockdata_free(saved_question); + + saved_question = blockdata_alloc((char *) header, (size_t)size); + saved_size = size; + + /* m > 0 if answered from cache */ + m = answer_request(header, ((char *) header) + 65536, (size_t)size, + dst_addr_4, netmask, now, ad_reqd, do_bit, have_pseudoheader, &stale, &filtered); + } /* Do this by steam now we're not in the select() loop */ check_log_writer(1); - if (m == 0) + if (m == 0 && saved_question) { struct server *master; int start; + blockdata_retrieve(saved_question, (size_t)saved_size, header); + size = saved_size; + if (lookup_domain(daemon->namebuff, gotname, &first, &last)) flags = is_local_answer(now, first, daemon->namebuff); else @@ -2362,9 +2420,10 @@ #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled && (master->flags & SERV_DO_DNSSEC)) { - int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */ + int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */ + int validatecount = daemon->limit[LIMIT_CRYPTO]; int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname, - serv, have_mark, mark, &keycount); + serv, have_mark, mark, &keycount, &validatecount); char *result, *domain = "result"; union all_addr a; @@ -2390,6 +2449,12 @@ } log_query(F_SECSTAT, domain, &a, result, 0); + + if ((daemon->limit[LIMIT_CRYPTO] - validatecount) > (int)daemon->metrics[METRIC_CRYPTO_HWM]) + daemon->metrics[METRIC_CRYPTO_HWM] = daemon->limit[LIMIT_CRYPTO] - validatecount; + + if ((daemon->limit[LIMIT_WORK] - keycount) > (int)daemon->metrics[METRIC_WORK_HWM]) + daemon->metrics[METRIC_WORK_HWM] = daemon->limit[LIMIT_WORK] - keycount; } #endif @@ -2431,13 +2496,23 @@ m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); } } - else if (stale) - { - u16 swap = htons((u16)EDE_STALE); - - m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); - } - + else if (have_pseudoheader) + { + ede = EDE_UNSET; + + if (filtered) + ede = EDE_FILTERED; + else if (stale) + ede = EDE_STALE; + + if (ede != EDE_UNSET) + { + u16 swap = htons((u16)ede); + + m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0); + } + } + check_log_writer(1); *length = htons(m); @@ -2453,7 +2528,7 @@ break; /* If we answered with stale data, this process will now try and get fresh data into - the cache then and cannot therefore accept new queries. Close the incoming + the cache and cannot therefore accept new queries. Close the incoming connection to signal that to the client. Then set do_stale and loop round once more to try and get fresh data, after which we exit. */ if (stale) @@ -2471,6 +2546,9 @@ close(confd); } + if (saved_question) + blockdata_free(saved_question); + return packet; } diff -Nru dnsmasq-2.89/src/hash-questions.c dnsmasq-2.90/src/hash-questions.c --- dnsmasq-2.89/src/hash-questions.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/hash-questions.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2020 Simon Kelley +/* Copyright (c) 2012-2023 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -165,7 +165,7 @@ WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) - m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + m[i] = (((WORD)data[j]) << 24) | (((WORD)data[j + 1]) << 16) | (((WORD)data[j + 2]) << 8) | (((WORD)data[j + 3])); for ( ; i < 64; ++i) m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; diff -Nru dnsmasq-2.89/src/helper.c dnsmasq-2.90/src/helper.c --- dnsmasq-2.89/src/helper.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/helper.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/inotify.c dnsmasq-2.90/src/inotify.c --- dnsmasq-2.89/src/inotify.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/inotify.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -94,7 +94,7 @@ if (daemon->inotifyfd == -1) die(_("failed to create inotify: %s"), NULL, EC_MISC); - if (option_bool(OPT_NO_RESOLV)) + if (daemon->port == 0 || option_bool(OPT_NO_RESOLV)) return; for (res = daemon->resolv_files; res; res = res->next) diff -Nru dnsmasq-2.89/src/ip6addr.h dnsmasq-2.90/src/ip6addr.h --- dnsmasq-2.89/src/ip6addr.h 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/ip6addr.h 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/lease.c dnsmasq-2.90/src/lease.c --- dnsmasq-2.89/src/lease.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/lease.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,7 +15,6 @@ */ #include "dnsmasq.h" - #ifdef HAVE_DHCP static struct dhcp_lease *leases = NULL, *old_leases = NULL; @@ -28,8 +27,7 @@ struct dhcp_lease *lease; int clid_len, hw_len, hw_type; int items; - char *domain = NULL; - + *daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0'; /* client-id max length is 255 which is 255*2 digits + 254 colons @@ -69,8 +67,8 @@ if (inet_pton(AF_INET, daemon->namebuff, &addr.addr4)) { - if ((lease = lease4_allocate(addr.addr4))) - domain = get_domain(lease->addr); + lease = lease4_allocate(addr.addr4); + hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type); /* For backwards compatibility, no explicit MAC address type means ether. */ @@ -90,10 +88,7 @@ } if ((lease = lease6_allocate(&addr.addr6, lease_type))) - { - lease_set_iaid(lease, strtoul(s, NULL, 10)); - domain = get_domain6(&lease->addr6); - } + lease_set_iaid(lease, strtoul(s, NULL, 10)); } #endif else @@ -114,7 +109,7 @@ hw_len, hw_type, clid_len, now, 0); if (strcmp(daemon->dhcp_buff, "*") != 0) - lease_set_hostname(lease, daemon->dhcp_buff, 0, domain, NULL); + lease_set_hostname(lease, daemon->dhcp_buff, 0, NULL, NULL); ei = atol(daemon->dhcp_buff3); @@ -946,6 +941,36 @@ lease->hostname = lease->fqdn = NULL; } +void lease_calc_fqdns(void) +{ + struct dhcp_lease *lease; + + for (lease = leases; lease; lease = lease->next) + { + char *domain; + + if (lease->hostname) + { +#ifdef HAVE_DHCP6 + if (lease->flags & (LEASE_TA | LEASE_NA)) + domain = get_domain6(&lease->addr6); + else +#endif + domain = get_domain(lease->addr); + + if (domain) + { + /* This is called only during startup, before forking, hence safe_malloc() */ + lease->fqdn = safe_malloc(strlen(lease->hostname) + strlen(domain) + 2); + + strcpy(lease->fqdn, lease->hostname); + strcat(lease->fqdn, "."); + strcat(lease->fqdn, domain); + } + } + } +} + void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain) { struct dhcp_lease *lease_tmp; diff -Nru dnsmasq-2.89/src/log.c dnsmasq-2.90/src/log.c --- dnsmasq-2.89/src/log.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/log.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/loop.c dnsmasq-2.90/src/loop.c --- dnsmasq-2.89/src/loop.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/loop.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -92,7 +92,7 @@ return 0; for (i = 0; i < 8; i++) - if (!isxdigit(query[i])) + if (!isxdigit((unsigned char)query[i])) return 0; uid = strtol(query, NULL, 16); diff -Nru dnsmasq-2.89/src/metrics.c dnsmasq-2.90/src/metrics.c --- dnsmasq-2.89/src/metrics.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/metrics.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,6 +24,9 @@ "dns_local_answered", "dns_stale_answered", "dns_unanswered", + "dnssec_max_crypto_use", + "dnssec_max_sig_fail", + "dnssec_max_work", "bootp", "pxe", "dhcp_ack", @@ -39,6 +42,7 @@ "leases_pruned_4", "leases_allocated_6", "leases_pruned_6", + "tcp_connections", }; const char* get_metric_name(int i) { diff -Nru dnsmasq-2.89/src/metrics.h dnsmasq-2.90/src/metrics.h --- dnsmasq-2.89/src/metrics.h 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/metrics.h 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,6 +23,9 @@ METRIC_DNS_LOCAL_ANSWERED, METRIC_DNS_STALE_ANSWERED, METRIC_DNS_UNANSWERED_QUERY, + METRIC_CRYPTO_HWM, + METRIC_SIG_FAIL_HWM, + METRIC_WORK_HWM, METRIC_BOOTP, METRIC_PXE, METRIC_DHCPACK, @@ -38,6 +41,7 @@ METRIC_LEASES_PRUNED_4, METRIC_LEASES_ALLOCATED_6, METRIC_LEASES_PRUNED_6, + METRIC_TCP_CONNECTIONS, __METRIC_MAX, }; diff -Nru dnsmasq-2.89/src/netlink.c dnsmasq-2.90/src/netlink.c --- dnsmasq-2.89/src/netlink.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/netlink.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/network.c dnsmasq-2.90/src/network.c --- dnsmasq-2.89/src/network.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/network.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -123,7 +123,10 @@ for (tmp = daemon->if_names; tmp; tmp = tmp->next) if (tmp->name && wildcard_match(tmp->name, name)) - ret = tmp->used = 1; + { + tmp->flags |= INAME_USED; + ret = 1; + } if (addr) for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) @@ -131,11 +134,17 @@ { if (family == AF_INET && tmp->addr.in.sin_addr.s_addr == addr->addr4.s_addr) - ret = match_addr = tmp->used = 1; + { + tmp->flags |= INAME_USED; + ret = match_addr = 1; + } else if (family == AF_INET6 && IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, &addr->addr6)) - ret = match_addr = tmp->used = 1; + { + tmp->flags |= INAME_USED; + ret = match_addr = 1; + } } } @@ -235,7 +244,8 @@ int loopback; struct ifreq ifr; int tftp_ok = !!option_bool(OPT_TFTP); - int dhcp_ok = 1; + int dhcp4_ok = 1; + int dhcp6_ok = 1; int auth_dns = 0; int is_label = 0; #if defined(HAVE_DHCP) || defined(HAVE_TFTP) @@ -251,7 +261,7 @@ loopback = ifr.ifr_flags & IFF_LOOPBACK; if (loopback) - dhcp_ok = 0; + dhcp4_ok = dhcp6_ok = 0; if (!label) label = ifr.ifr_name; @@ -359,13 +369,8 @@ struct in_addr newaddr = addr->in.sin_addr; if (int_name->flags & INP4) - { - if (netmask.s_addr == 0xffffffff) - continue; - - newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) | - (int_name->proto4.s_addr & ~netmask.s_addr); - } + newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) | + (int_name->proto4.s_addr & ~netmask.s_addr); /* check for duplicates. */ for (lp = int_name->addr; lp; lp = lp->next) @@ -398,10 +403,6 @@ { int i; - /* No sense in doing /128. */ - if (prefixlen == 128) - continue; - for (i = 0; i < 16; i++) { int bits = ((i+1)*8) - prefixlen; @@ -510,7 +511,7 @@ if ((lo->name = whine_malloc(strlen(ifr.ifr_name)+1))) { strcpy(lo->name, ifr.ifr_name); - lo->used = 1; + lo->flags |= INAME_USED; lo->next = daemon->if_names; daemon->if_names = lo; } @@ -532,14 +533,17 @@ if (auth_dns) { tftp_ok = 0; - dhcp_ok = 0; + dhcp4_ok = dhcp6_ok = 0; } else for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) { tftp_ok = 0; - dhcp_ok = 0; + if (tmp->flags & INAME_4) + dhcp4_ok = 0; + if (tmp->flags & INAME_6) + dhcp6_ok = 0; } #endif @@ -566,7 +570,8 @@ iface->addr = *addr; iface->netmask = netmask; iface->tftp_ok = tftp_ok; - iface->dhcp_ok = dhcp_ok; + iface->dhcp4_ok = dhcp4_ok; + iface->dhcp6_ok = dhcp6_ok; iface->dns_auth = auth_dns; iface->mtu = mtu; iface->dad = !!(iface_flags & IFACE_TENTATIVE); @@ -915,15 +920,24 @@ errno = errsave; - if (dienow) + /* Failure to bind addresses given by --listen-address at this point + because there's no interface with the address is OK if we're doing bind-dynamic. + If/when an interface is created with the relevant address we'll notice + and attempt to bind it then. This is in the generic error path so we close the socket, + but EADDRNOTAVAIL is only a possible error from bind() + + When a new address is created and we call this code again (dienow == 0) there + may still be configured addresses when don't exist, (consider >1 --listen-address, + when the first is created, the second will still be missing) so we suppress + EADDRNOTAVAIL even in that case to avoid confusing log entries. + */ + if (!option_bool(OPT_CLEVERBIND) || errno != EADDRNOTAVAIL) { - /* failure to bind addresses given by --listen-address at this point - is OK if we're doing bind-dynamic */ - if (!option_bool(OPT_CLEVERBIND)) + if (dienow) die(s, daemon->addrbuff, EC_BADNET); + else + my_syslog(LOG_WARNING, s, daemon->addrbuff, strerror(errno)); } - else - my_syslog(LOG_WARNING, s, daemon->addrbuff, strerror(errno)); return -1; } @@ -1207,7 +1221,7 @@ (no netmask) and some MTU login the tftp code. */ for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next) - if (!if_tmp->used && + if (!(if_tmp->flags & INAME_USED) && (new = create_listeners(&if_tmp->addr, !!option_bool(OPT_TFTP), dienow))) { new->next = daemon->listeners; @@ -1295,7 +1309,7 @@ struct irec *iface, *tmp; for (iface = daemon->interfaces; iface; iface = iface->next) - if (iface->addr.sa.sa_family == AF_INET6 && iface->dhcp_ok && !iface->multicast_done) + if (iface->addr.sa.sa_family == AF_INET6 && iface->dhcp6_ok && !iface->multicast_done) { /* There's an irec per address but we only want to join for multicast once per interface. Weed out duplicates. */ diff -Nru dnsmasq-2.89/src/nftset.c dnsmasq-2.90/src/nftset.c --- dnsmasq-2.89/src/nftset.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/nftset.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -43,7 +43,8 @@ const char *cmd = remove ? cmd_del : cmd_add; int ret, af = (flags & F_IPV4) ? AF_INET : AF_INET6; size_t new_sz; - char *new, *err, *nl; + char *err_str, *new, *nl; + const char *err; static char *cmd_buf = NULL; static size_t cmd_buf_sz = 0; @@ -78,14 +79,19 @@ } ret = nft_run_cmd_from_buffer(ctx, cmd_buf); - err = (char *)nft_ctx_get_error_buffer(ctx); + err = nft_ctx_get_error_buffer(ctx); if (ret != 0) { /* Log only first line of error return. */ - if ((nl = strchr(err, '\n'))) - *nl = 0; - my_syslog(LOG_ERR, "nftset %s %s", setname, err); + if ((err_str = whine_malloc(strlen(err) + 1))) + { + strcpy(err_str, err); + if ((nl = strchr(err_str, '\n'))) + *nl = 0; + my_syslog(LOG_ERR, "nftset %s %s", setname, err_str); + free(err_str); + } } return ret; diff -Nru dnsmasq-2.89/src/option.c dnsmasq-2.90/src/option.c --- dnsmasq-2.89/src/option.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/option.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -186,6 +186,12 @@ #define LOPT_STALE_CACHE 377 #define LOPT_NORR 378 #define LOPT_NO_IDENT 379 +#define LOPT_CACHE_RR 380 +#define LOPT_FILTER_RR 381 +#define LOPT_NO_DHCP6 382 +#define LOPT_NO_DHCP4 383 +#define LOPT_MAX_PROCS 384 +#define LOPT_DNSSEC_LIMITS 385 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -217,7 +223,7 @@ { "domain-suffix", 1, 0, 's' }, { "interface", 1, 0, 'i' }, { "listen-address", 1, 0, 'a' }, - { "local-service", 0, 0, LOPT_LOCAL_SERVICE }, + { "local-service", 2, 0, LOPT_LOCAL_SERVICE }, { "bogus-priv", 0, 0, 'b' }, { "bogus-nxdomain", 1, 0, 'B' }, { "ignore-address", 1, 0, LOPT_IGNORE_ADDR }, @@ -225,6 +231,7 @@ { "filterwin2k", 0, 0, 'f' }, { "filter-A", 0, 0, LOPT_FILTER_A }, { "filter-AAAA", 0, 0, LOPT_FILTER_AAAA }, + { "filter-rr", 1, 0, LOPT_FILTER_RR }, { "pid-file", 2, 0, 'x' }, { "strict-order", 0, 0, 'o' }, { "server", 1, 0, 'S' }, @@ -239,11 +246,14 @@ { "local-ttl", 1, 0, 'T' }, { "no-negcache", 0, 0, 'N' }, { "no-round-robin", 0, 0, LOPT_NORR }, + { "cache-rr", 1, 0, LOPT_CACHE_RR }, { "addn-hosts", 1, 0, 'H' }, { "hostsdir", 1, 0, LOPT_HOST_INOTIFY }, { "query-port", 1, 0, 'Q' }, { "except-interface", 1, 0, 'I' }, { "no-dhcp-interface", 1, 0, '2' }, + { "no-dhcpv4-interface", 1, 0, LOPT_NO_DHCP4 }, + { "no-dhcpv6-interface", 1, 0, LOPT_NO_DHCP6 }, { "domain-needed", 0, 0, 'D' }, { "dhcp-lease-max", 1, 0, 'X' }, { "bind-interfaces", 0, 0, 'z' }, @@ -355,6 +365,7 @@ { "dnssec-check-unsigned", 2, 0, LOPT_DNSSEC_CHECK }, { "dnssec-no-timecheck", 0, 0, LOPT_DNSSEC_TIME }, { "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP }, + { "dnssec-limits", 1, 0, LOPT_DNSSEC_LIMITS }, { "dhcp-relay", 1, 0, LOPT_RELAY }, { "ra-param", 1, 0, LOPT_RA_PARAM }, { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP }, @@ -376,6 +387,7 @@ { "fast-dns-retry", 2, 0, LOPT_FAST_RETRY }, { "use-stale-cache", 2, 0 , LOPT_STALE_CACHE }, { "no-ident", 0, 0, LOPT_NO_IDENT }, + { "max-tcp-connections", 1, 0, LOPT_MAX_PROCS }, { NULL, 0, 0, 0 } }; @@ -403,8 +415,9 @@ { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL }, { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL }, { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL }, - { LOPT_FILTER_A, OPT_FILTER_A, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL }, - { LOPT_FILTER_AAAA, OPT_FILTER_AAAA, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL }, + { LOPT_FILTER_A, ARG_DUP, NULL, gettext_noop("Don't include IPv4 addresses in DNS answers."), NULL }, + { LOPT_FILTER_AAAA, ARG_DUP, NULL, gettext_noop("Don't include IPv6 addresses in DNS answers."), NULL }, + { LOPT_FILTER_RR, ARG_DUP, "", gettext_noop("Don't include resource records of the given type in DNS answers."), NULL }, { 'F', ARG_DUP, ",...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL }, { 'g', ARG_ONE, "", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP }, { 'G', ARG_DUP, "", gettext_noop("Set address or hostname for a specified machine."), NULL }, @@ -473,6 +486,8 @@ { '1', ARG_ONE, "[=]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL }, { LOPT_UBUS, ARG_ONE, "[=]", gettext_noop("Enable the UBus interface."), NULL }, { '2', ARG_DUP, "", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL }, + { LOPT_NO_DHCP6, ARG_DUP, "", gettext_noop("Do not provide DHCPv6 on this interface."), NULL }, + { LOPT_NO_DHCP4, ARG_DUP, "", gettext_noop("Do not provide DHCPv4 on this interface."), NULL }, { '3', ARG_DUP, "[=tag:]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL }, { '4', ARG_DUP, "set:,", gettext_noop("Map MAC address (with wildcards) to option set."), NULL }, { LOPT_BRIDGE, ARG_DUP, ",..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL }, @@ -555,24 +570,27 @@ { LOPT_DNSSEC_CHECK, ARG_DUP, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL }, { LOPT_DNSSEC_TIME, OPT_DNSSEC_TIME, NULL, gettext_noop("Don't check DNSSEC signature timestamps until first cache-reload"), NULL }, { LOPT_DNSSEC_STAMP, ARG_ONE, "", gettext_noop("Timestamp file to verify system clock for DNSSEC"), NULL }, + { LOPT_DNSSEC_LIMITS, ARG_ONE, ",..", gettext_noop("Set resource limits for DNSSEC validation"), NULL }, { LOPT_RA_PARAM, ARG_DUP, ",[mtu:||off,][,][,]", gettext_noop("Set MTU, priority, resend-interval and router-lifetime"), NULL }, { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL }, { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL }, { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL }, { LOPT_LOG_DEBUG, OPT_LOG_DEBUG, NULL, gettext_noop("Log debugging information."), NULL }, - { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL }, + { LOPT_LOCAL_SERVICE, ARG_ONE, NULL, gettext_noop("Accept queries only from directly-connected networks."), NULL }, { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops."), NULL }, { LOPT_IGNORE_ADDR, ARG_DUP, "", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, { LOPT_DHCPTTL, ARG_ONE, "", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, { LOPT_REPLY_DELAY, ARG_ONE, "", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL }, { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL }, - { LOPT_DUMPFILE, ARG_ONE, "", gettext_noop("Path to debug packet dump file"), NULL }, - { LOPT_DUMPMASK, ARG_ONE, "", gettext_noop("Mask which packets to dump"), NULL }, + { LOPT_DUMPFILE, ARG_ONE, "", gettext_noop("Path to debug packet dump file."), NULL }, + { LOPT_DUMPMASK, ARG_ONE, "", gettext_noop("Mask which packets to dump."), NULL }, { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL }, { LOPT_UMBRELLA, ARG_ONE, "[=]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL }, { LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL }, { LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL }, { LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL }, + { LOPT_CACHE_RR, ARG_DUP, "", gettext_noop("Cache this DNS resource record type."), NULL }, + { LOPT_MAX_PROCS, ARG_ONE, "", gettext_noop("Maximum number of concurrent tcp connections."), NULL }, { 0, 0, NULL, NULL, NULL } }; @@ -1159,6 +1177,9 @@ } else { + /* Always reset server as valid here, so we can add the same upstream + server address multiple times for each x.y.z.in-addr.arpa */ + sdetails.valid = 1; while (parse_server_next(&sdetails)) { if ((string = parse_server_addr(&sdetails))) @@ -1244,6 +1265,9 @@ } else { + /* Always reset server as valid here, so we can add the same upstream + server address multiple times for each x.y.z.ip6.arpa */ + sdetails.valid = 1; while (parse_server_next(&sdetails)) { if ((string = parse_server_addr(&sdetails))) @@ -1261,6 +1285,17 @@ return NULL; } +static void if_names_add(const char *ifname) +{ + struct iname *new = opt_malloc(sizeof(struct iname)); + new->next = daemon->if_names; + daemon->if_names = new; + /* new->name may be NULL if someone does + "interface=" to disable all interfaces except loop. */ + new->name = opt_string_alloc(ifname); + new->flags = 0; +} + #ifdef HAVE_DHCP static int is_tag_prefix(char *arg) @@ -1390,7 +1425,6 @@ free(opt); } - /* This is too insanely large to keep in-line in the switch */ static int parse_dhcp_opt(char *errstr, char *arg, int flags) { @@ -2556,179 +2590,182 @@ case 's': /* --domain */ case LOPT_SYNTH: /* --synth-domain */ - if (strcmp (arg, "#") == 0) - set_option_bool(OPT_RESOLV_DOMAIN); - else - { - char *d, *d_raw = arg; - comma = split(arg); - if (!(d = canonicalise_opt(d_raw))) - ret_err(gen_err); - else - { - free(d); /* allocate this again below. */ - if (comma) - { - struct cond_domain *new = opt_malloc(sizeof(struct cond_domain)); - char *netpart; - - new->prefix = NULL; - new->indexed = 0; - new->prefixlen = 0; - - unhide_metas(comma); - if ((netpart = split_chr(comma, '/'))) - { - int msize; - - arg = split(netpart); - if (!atoi_check(netpart, &msize)) - ret_err_free(gen_err, new); - else if (inet_pton(AF_INET, comma, &new->start)) - { - int mask; - - if (msize > 32) - ret_err_free(_("bad prefix length"), new); - - mask = (1 << (32 - msize)) - 1; - new->is6 = 0; - new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask); - new->end.s_addr = new->start.s_addr | htonl(mask); - if (arg) - { - if (option != 's') - { - if (!(new->prefix = canonicalise_opt(arg)) || - strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) - ret_err_free(_("bad prefix"), new); - } - else if (strcmp(arg, "local") != 0) - ret_err_free(gen_err, new); - else - { - /* local=/xxx.yyy.zzz.in-addr.arpa/ */ - domain_rev4(0, NULL, &new->start, msize); - - /* local=// */ - /* d_raw can't failed to canonicalise here, checked above. */ - add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL); - } - } - } - else if (inet_pton(AF_INET6, comma, &new->start6)) - { - u64 mask, addrpart = addr6part(&new->start6); - - if (msize > 128) - ret_err_free(_("bad prefix length"), new); - - mask = (1LLU << (128 - msize)) - 1LLU; - - new->is6 = 1; - new->prefixlen = msize; - - /* prefix==64 overflows the mask calculation above */ - if (msize <= 64) - mask = (u64)-1LL; - - new->end6 = new->start6; - setaddr6part(&new->start6, addrpart & ~mask); - setaddr6part(&new->end6, addrpart | mask); + { + char *d, *d_raw = arg; + comma = split(arg); + if (!(d = canonicalise_opt(d_raw))) + ret_err(gen_err); + else + { + free(d); /* allocate this again below. */ + if (comma) + { + struct cond_domain *new = opt_malloc(sizeof(struct cond_domain)); + char *netpart; + + new->prefix = NULL; + new->indexed = 0; + new->prefixlen = 0; + + unhide_metas(comma); + if ((netpart = split_chr(comma, '/'))) + { + int msize; + + arg = split(netpart); + if (!atoi_check(netpart, &msize)) + ret_err_free(gen_err, new); + else if (inet_pton(AF_INET, comma, &new->start)) + { + int mask; + + if (msize > 32) + ret_err_free(_("bad prefix length"), new); + + mask = (1 << (32 - msize)) - 1; + new->is6 = 0; + new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask); + new->end.s_addr = new->start.s_addr | htonl(mask); + if (arg) + { + if (option != 's') + { + if (!(new->prefix = canonicalise_opt(arg)) || + strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) + ret_err_free(_("bad prefix"), new); + } + else if (strcmp(arg, "local") != 0) + ret_err_free(gen_err, new); + else + { + /* local=/xxx.yyy.zzz.in-addr.arpa/ */ + domain_rev4(0, NULL, &new->start, msize); + + /* local=// */ + /* d_raw can't failed to canonicalise here, checked above. */ + add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL); + } + } + } + else if (inet_pton(AF_INET6, comma, &new->start6)) + { + u64 mask, addrpart = addr6part(&new->start6); + + if (msize > 128) + ret_err_free(_("bad prefix length"), new); + + mask = (1LLU << (128 - msize)) - 1LLU; + + new->is6 = 1; + new->prefixlen = msize; + + /* prefix==64 overflows the mask calculation above */ + if (msize <= 64) + mask = (u64)-1LL; - if (arg) - { - if (option != 's') - { - if (!(new->prefix = canonicalise_opt(arg)) || - strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN) - ret_err_free(_("bad prefix"), new); - } - else if (strcmp(arg, "local") != 0) - ret_err_free(gen_err, new); - else - { - /* generate the equivalent of - local=/xxx.yyy.zzz.ip6.arpa/ */ - domain_rev6(0, NULL, &new->start6, msize); - - /* local=// */ - /* d_raw can't failed to canonicalise here, checked above. */ - add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL); - } - } - } - else - ret_err_free(gen_err, new); - } - else - { - char *prefstr; - arg = split(comma); - prefstr = split(arg); - - if (inet_pton(AF_INET, comma, &new->start)) - { - new->is6 = 0; - if (!arg) - new->end.s_addr = new->start.s_addr; - else if (!inet_pton(AF_INET, arg, &new->end)) - ret_err_free(gen_err, new); - } - else if (inet_pton(AF_INET6, comma, &new->start6)) - { - new->is6 = 1; - if (!arg) - memcpy(&new->end6, &new->start6, IN6ADDRSZ); - else if (!inet_pton(AF_INET6, arg, &new->end6)) - ret_err_free(gen_err, new); - } - else if (option == 's') - { - /* subnet from interface. */ - new->interface = opt_string_alloc(comma); - new->al = NULL; - } - else - ret_err_free(gen_err, new); - - if (option != 's' && prefstr) - { - if (!(new->prefix = canonicalise_opt(prefstr)) || - strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) - ret_err_free(_("bad prefix"), new); - } - } - - new->domain = canonicalise_opt(d_raw); - if (option == 's') - { - new->next = daemon->cond_domain; - daemon->cond_domain = new; - } - else - { - char *star; - if (new->prefix && - (star = strrchr(new->prefix, '*')) - && *(star+1) == 0) - { - *star = 0; - new->indexed = 1; - if (new->is6 && new->prefixlen < 64) - ret_err_free(_("prefix length too small"), new); - } - new->next = daemon->synth_domains; - daemon->synth_domains = new; - } - } - else if (option == 's') - daemon->domain_suffix = canonicalise_opt(d_raw); - else - ret_err(gen_err); - } - } - break; + new->end6 = new->start6; + setaddr6part(&new->start6, addrpart & ~mask); + setaddr6part(&new->end6, addrpart | mask); + + if (arg) + { + if (option != 's') + { + if (!(new->prefix = canonicalise_opt(arg)) || + strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN) + ret_err_free(_("bad prefix"), new); + } + else if (strcmp(arg, "local") != 0) + ret_err_free(gen_err, new); + else + { + /* generate the equivalent of + local=/xxx.yyy.zzz.ip6.arpa/ */ + domain_rev6(0, NULL, &new->start6, msize); + + /* local=// */ + /* d_raw can't failed to canonicalise here, checked above. */ + add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL); + } + } + } + else + ret_err_free(gen_err, new); + } + else + { + char *prefstr; + arg = split(comma); + prefstr = split(arg); + + if (inet_pton(AF_INET, comma, &new->start)) + { + new->is6 = 0; + if (!arg) + new->end.s_addr = new->start.s_addr; + else if (!inet_pton(AF_INET, arg, &new->end)) + ret_err_free(gen_err, new); + } + else if (inet_pton(AF_INET6, comma, &new->start6)) + { + new->is6 = 1; + if (!arg) + memcpy(&new->end6, &new->start6, IN6ADDRSZ); + else if (!inet_pton(AF_INET6, arg, &new->end6)) + ret_err_free(gen_err, new); + } + else if (option == 's') + { + /* subnet from interface. */ + new->interface = opt_string_alloc(comma); + new->al = NULL; + } + else + ret_err_free(gen_err, new); + + if (option != 's' && prefstr) + { + if (!(new->prefix = canonicalise_opt(prefstr)) || + strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN) + ret_err_free(_("bad prefix"), new); + } + } + + new->domain = canonicalise_opt(d_raw); + if (option == 's') + { + new->next = daemon->cond_domain; + daemon->cond_domain = new; + } + else + { + char *star; + if (new->prefix && + (star = strrchr(new->prefix, '*')) + && *(star+1) == 0) + { + *star = 0; + new->indexed = 1; + if (new->is6 && new->prefixlen < 64) + ret_err_free(_("prefix length too small"), new); + } + new->next = daemon->synth_domains; + daemon->synth_domains = new; + } + } + else if (option == 's') + { + if (strcmp (arg, "#") == 0) + set_option_bool(OPT_RESOLV_DOMAIN); + else + daemon->domain_suffix = canonicalise_opt(d_raw); + } + else + ret_err(gen_err); + } + + break; + } case LOPT_CPE_ID: /* --add-dns-client */ if (arg) @@ -2751,7 +2788,7 @@ ret_err(gen_err); for (p = arg; *p; p++) - if (!isxdigit((int)*p)) + if (!isxdigit((unsigned char)*p)) ret_err(gen_err); set_option_bool(OPT_UMBRELLA_DEVID); @@ -2811,14 +2848,8 @@ case 'i': /* --interface */ do { - struct iname *new = opt_malloc(sizeof(struct iname)); - comma = split(arg); - new->next = daemon->if_names; - daemon->if_names = new; - /* new->name may be NULL if someone does - "interface=" to disable all interfaces except loop. */ - new->name = opt_string_alloc(arg); - new->used = 0; + comma = split(arg); + if_names_add(arg); arg = comma; } while (arg); break; @@ -2831,10 +2862,13 @@ case 'I': /* --except-interface */ case '2': /* --no-dhcp-interface */ + case LOPT_NO_DHCP6: /* --no-dhcpv6-interface */ + case LOPT_NO_DHCP4: /* --no-dhcpv4-interface */ do { struct iname *new = opt_malloc(sizeof(struct iname)); comma = split(arg); new->name = opt_string_alloc(arg); + new->flags = INAME_4 | INAME_6; if (option == 'I') { new->next = daemon->if_except; @@ -2847,6 +2881,10 @@ } else { + if (option == LOPT_NO_DHCP6) + new->flags &= ~INAME_4; + if (option == LOPT_NO_DHCP4) + new->flags &= ~INAME_6; new->next = daemon->dhcp_except; daemon->dhcp_except = new; } @@ -2928,7 +2966,7 @@ else ret_err_free(gen_err, new); - new->used = 0; + new->flags = 0; if (option == 'a') { new->next = daemon->if_addrs; @@ -3043,8 +3081,8 @@ else flags &= ~SERV_FOR_NODOTS; - /* address=/#/ matches the same as without domain */ - if (option == 'A' && cur_domain[0] == '#' && cur_domain[1] == 0) + /* address=/#/ matches the same as without domain, as does server=/#/.... for consistency. */ + if (cur_domain[0] == '#' && cur_domain[1] == 0) cur_domain[0] = 0; } @@ -3377,6 +3415,15 @@ ret_err(gen_err); else if (daemon->max_logs > 100) daemon->max_logs = 100; + break; + + case LOPT_LOCAL_SERVICE: /* --local-service */ + if (!arg || !strcmp(arg, "net")) + set_option_bool(OPT_LOCAL_SERVICE); + else if (!strcmp(arg, "host")) + set_option_bool(OPT_LOCALHOST_SERVICE); + else + ret_err(gen_err); break; case 'P': /* --edns-packet-max */ @@ -3437,7 +3484,7 @@ break; } - case LOPT_FAST_RETRY: + case LOPT_FAST_RETRY: /* --fast-dns-retry */ daemon->fast_retry_timeout = TIMEOUT; if (!arg) @@ -3459,6 +3506,47 @@ } } break; + + case LOPT_CACHE_RR: /* --cache-rr */ + case LOPT_FILTER_RR: /* --filter-rr */ + case LOPT_FILTER_A: /* --filter-A */ + case LOPT_FILTER_AAAA: /* --filter-AAAA */ + while (1) { + int type; + struct rrlist *new; + + comma = NULL; + + if (option == LOPT_FILTER_A) + type = T_A; + else if (option == LOPT_FILTER_AAAA) + type = T_AAAA; + else + { + comma = split(arg); + if (!atoi_check(arg, &type) && (type = rrtype(arg)) == 0) + ret_err(_("bad RR type")); + } + + new = opt_malloc(sizeof(struct rrlist)); + new->rr = type; + + if (option == LOPT_CACHE_RR) + { + new->next = daemon->cache_rr; + daemon->cache_rr = new; + } + else + { + new->next = daemon->filter_rr; + daemon->filter_rr = new; + } + + if (!comma) break; + arg = comma; + } + break; + #ifdef HAVE_DHCP case 'X': /* --dhcp-lease-max */ @@ -4836,7 +4924,7 @@ new->target = target; new->ttl = ttl; - for (arg += arglen+1; *arg && isspace(*arg); arg++); + for (arg += arglen+1; *arg && isspace((unsigned char)*arg); arg++); } break; @@ -5154,7 +5242,7 @@ break; } - case LOPT_STALE_CACHE: + case LOPT_STALE_CACHE: /* --use-stale-cache */ { int max_expiry = STALE_CACHE_EXPIRY; if (arg) @@ -5173,6 +5261,24 @@ } #ifdef HAVE_DNSSEC + case LOPT_DNSSEC_LIMITS: + { + int lim, val; + + for (lim = LIMIT_SIG_FAIL; arg && lim < LIMIT_MAX ; lim++, arg = comma) + { + comma = split(arg); + + if (!atoi_check(arg, &val)) + ret_err(gen_err); + + if (val != 0) + daemon->limit[lim] = val; + } + + break; + } + case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */ daemon->timestamp_file = opt_string_alloc(arg); break; @@ -5227,7 +5333,7 @@ unhide_metas(keyhex); /* 4034: "Whitespace is allowed within digits" */ for (cp = keyhex; *cp; ) - if (isspace(*cp)) + if (isspace((unsigned char)*cp)) for (cp1 = cp; *cp1; cp1++) *cp1 = *(cp1+1); else @@ -5244,7 +5350,17 @@ break; } #endif - + + case LOPT_MAX_PROCS: /* --max-tcp-connections */ + { + int max_procs; + /* Don't accept numbers less than 1. */ + if (!atoi_check(arg, &max_procs) || max_procs < 1) + ret_err(gen_err); + daemon->max_procs = max_procs; + break; + } + default: ret_err(_("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DNSSEC/DBus support)")); @@ -5315,7 +5431,7 @@ memmove(p, p+1, strlen(p+1)+1); } - if (isspace(*p)) + if (isspace((unsigned char)*p)) { *p = ' '; white = 1; @@ -5665,11 +5781,11 @@ } } -static void clear_dynamic_opt(void) +static void clear_dhcp_opt(struct dhcp_opt **dhcp_opts) { struct dhcp_opt *opts, *cp, **up; - for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp) + for (up = dhcp_opts, opts = *dhcp_opts; opts; opts = cp) { cp = opts->next; @@ -5683,6 +5799,14 @@ } } +static void clear_dynamic_opt(void) +{ + clear_dhcp_opt(&daemon->dhcp_opts); +#ifdef HAVE_DHCP6 + clear_dhcp_opt(&daemon->dhcp_opts6); +#endif +} + void reread_dhcp(void) { struct hostsfile *hf; @@ -5727,15 +5851,21 @@ { size_t argbuf_size = MAXDNAME; char *argbuf = opt_malloc(argbuf_size); - char *buff = opt_malloc(MAXDNAME); + /* Note that both /000 and '.' are allowed within labels. These get + represented in presentation format using NAME_ESCAPE as an escape + character. In theory, if all the characters in a name were /000 or + '.' or NAME_ESCAPE then all would have to be escaped, so the + presentation format would be twice as long as the spec. */ + char *buff = opt_malloc((MAXDNAME * 2) + 1); int option, testmode = 0; char *arg, *conffile = NULL; - + opterr = 0; daemon = opt_malloc(sizeof(struct daemon)); memset(daemon, 0, sizeof(struct daemon)); daemon->namebuff = buff; + daemon->workspacename = safe_malloc((MAXDNAME * 2) + 1); daemon->addrbuff = safe_malloc(ADDRSTRLEN); /* Set defaults - everything else is zero or NULL */ @@ -5759,6 +5889,13 @@ daemon->soa_expiry = SOA_EXPIRY; daemon->randport_limit = 1; daemon->host_index = SRC_AH; + daemon->max_procs = MAX_PROCS; +#ifdef HAVE_DNSSEC + daemon->limit[LIMIT_SIG_FAIL] = DNSSEC_LIMIT_SIG_FAIL; + daemon->limit[LIMIT_CRYPTO] = DNSSEC_LIMIT_CRYPTO; + daemon->limit[LIMIT_WORK] = DNSSEC_LIMIT_WORK; + daemon->limit[LIMIT_NSEC3_ITERS] = DNSSEC_LIMIT_NSEC3_ITERS; +#endif /* See comment above make_servers(). Optimises server-read code. */ mark_servers(0); @@ -6056,7 +6193,16 @@ /* If there's access-control config, then ignore --local-service, it's intended as a system default to keep otherwise unconfigured installations safe. */ if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver) - reset_option_bool(OPT_LOCAL_SERVICE); + { + reset_option_bool(OPT_LOCAL_SERVICE); + reset_option_bool(OPT_LOCALHOST_SERVICE); + } + else if (option_bool(OPT_LOCALHOST_SERVICE) && !option_bool(OPT_LOCAL_SERVICE)) + { + /* listen only on localhost, emulate --interface=lo --bind-interfaces */ + if_names_add(NULL); + set_option_bool(OPT_NOWILD); + } if (testmode) { diff -Nru dnsmasq-2.89/src/outpacket.c dnsmasq-2.90/src/outpacket.c --- dnsmasq-2.89/src/outpacket.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/outpacket.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/pattern.c dnsmasq-2.90/src/pattern.c --- dnsmasq-2.89/src/pattern.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/pattern.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/poll.c dnsmasq-2.90/src/poll.c --- dnsmasq-2.89/src/poll.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/poll.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/radv-protocol.h dnsmasq-2.90/src/radv-protocol.h --- dnsmasq-2.89/src/radv-protocol.h 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/radv-protocol.h 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/radv.c dnsmasq-2.90/src/radv.c --- dnsmasq-2.89/src/radv.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/radv.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -186,7 +186,8 @@ return; for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) - if (tmp->name && wildcard_match(tmp->name, interface)) + if (tmp->name && (tmp->flags & INAME_6) && + wildcard_match(tmp->name, interface)) return; if (packet[1] != 0) @@ -835,7 +836,8 @@ { struct iname *tmp; for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) - if (tmp->name && wildcard_match(tmp->name, param.name)) + if (tmp->name && (tmp->flags & INAME_6) && + wildcard_match(tmp->name, param.name)) break; if (!tmp) { @@ -934,7 +936,8 @@ return 1; for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) - if (tmp->name && wildcard_match(tmp->name, param->name)) + if (tmp->name && (tmp->flags & INAME_6) && + wildcard_match(tmp->name, param->name)) return 1; for (context = daemon->dhcp6; context; context = context->next) diff -Nru dnsmasq-2.89/src/rfc1035.c dnsmasq-2.90/src/rfc1035.c --- dnsmasq-2.89/src/rfc1035.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/rfc1035.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -89,23 +89,14 @@ if (isExtract) { unsigned char c = *p; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID)) + + if (c == 0 || c == '.' || c == NAME_ESCAPE) { - if (c == 0 || c == '.' || c == NAME_ESCAPE) - { - *cp++ = NAME_ESCAPE; - *cp++ = c+1; - } - else - *cp++ = c; + *cp++ = NAME_ESCAPE; + *cp++ = c+1; } else -#endif - if (c != 0 && c != '.') - *cp++ = c; - else - return 0; + *cp++ = c; } else { @@ -118,10 +109,9 @@ cp++; if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE) + + if (c1 == NAME_ESCAPE) c1 = (*cp++)-1; -#endif if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; @@ -394,14 +384,23 @@ ((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */ } -static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, int *doctored) +int do_doctor(struct dns_header *header, size_t qlen, char *namebuff) { + unsigned char *p; int i, qtype, qclass, rdlen; - - for (i = count; i != 0; i--) + int done = 0; + + if (!(p = skip_questions(header, qlen))) + return done; + + for (i = 0; i < ntohs(header->ancount) + ntohs(header->arcount); i++) { - if (!(p = skip_name(p, header, qlen, 10))) - return 0; /* bad packet */ + /* Skip over auth section */ + if (i == ntohs(header->ancount) && !(p = skip_section(p, ntohs(header->nscount), header, qlen))) + return done; + + if (!extract_name(header, qlen, &p, namebuff, 1, 10)) + return done; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); @@ -411,103 +410,193 @@ if (qclass == C_IN && qtype == T_A) { struct doctor *doctor; - struct in_addr addr; + union all_addr addr; if (!CHECK_LEN(header, p, qlen, INADDRSZ)) - return 0; + return done; /* alignment */ - memcpy(&addr, p, INADDRSZ); + memcpy(&addr.addr4, p, INADDRSZ); for (doctor = daemon->doctors; doctor; doctor = doctor->next) { if (doctor->end.s_addr == 0) { - if (!is_same_net(doctor->in, addr, doctor->mask)) + if (!is_same_net(doctor->in, addr.addr4, doctor->mask)) continue; } - else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) || - ntohl(doctor->end.s_addr) < ntohl(addr.s_addr)) + else if (ntohl(doctor->in.s_addr) > ntohl(addr.addr4.s_addr) || + ntohl(doctor->end.s_addr) < ntohl(addr.addr4.s_addr)) continue; - addr.s_addr &= ~doctor->mask.s_addr; - addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr); + addr.addr4.s_addr &= ~doctor->mask.s_addr; + addr.addr4.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr); /* Since we munged the data, the server it came from is no longer authoritative */ header->hb3 &= ~HB3_AA; - *doctored = 1; - memcpy(p, &addr, INADDRSZ); +#ifdef HAVE_DNSSEC + /* remove validated flag from this RR, since we changed it! */ + if (option_bool(OPT_DNSSEC_VALID) && i < ntohs(header->ancount)) + daemon->rr_status[i] = 0; +#endif + done = 1; + memcpy(p, &addr.addr4, INADDRSZ); + log_query(F_FORWARD | F_CONFIG | F_IPV4, namebuff, &addr, NULL, 0); break; } } if (!ADD_RDLEN(header, p, qlen, rdlen)) - return 0; /* bad packet */ + return done; /* bad packet */ } - - return p; + + return done; } -static int find_soa(struct dns_header *header, size_t qlen, int *doctored) +/* Find SOA RR in auth section to get TTL for negative caching of name. + Cache said SOA and return the difference in length between name and the name of the + SOA RR so we can look it up again. +*/ +static int find_soa(struct dns_header *header, size_t qlen, char *name, int *substring, unsigned long *ttlp, int no_cache, time_t now) { - unsigned char *p; + unsigned char *p, *psave; int qtype, qclass, rdlen; - unsigned long ttl, minttl = ULONG_MAX; - int i, found_soa = 0; - - /* first move to NS section and find TTL from any SOA section */ + unsigned long ttl, minttl; + int i, j; + size_t name_len, soa_len, len; + union all_addr addr; + + /* first move to NS section and find TTL from SOA RR */ if (!(p = skip_questions(header, qlen)) || - !(p = do_doctor(p, ntohs(header->ancount), header, qlen, doctored))) + !(p = skip_section(p, ntohs(header->ancount), header, qlen))) return 0; /* bad packet */ + + name_len = strlen(name); - for (i = ntohs(header->nscount); i != 0; i--) + if (substring) + *substring = name_len; + + if (ttlp) + *ttlp = daemon->neg_ttl; + + for (i = 0; i < ntohs(header->nscount); i++) { - if (!(p = skip_name(p, header, qlen, 10))) + if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 0)) return 0; /* bad packet */ GETSHORT(qtype, p); GETSHORT(qclass, p); GETLONG(ttl, p); GETSHORT(rdlen, p); + + psave = p; if ((qclass == C_IN) && (qtype == T_SOA)) { - found_soa = 1; - if (ttl < minttl) - minttl = ttl; + soa_len = strlen(daemon->workspacename); - /* MNAME */ - if (!(p = skip_name(p, header, qlen, 0))) - return 0; - /* RNAME */ - if (!(p = skip_name(p, header, qlen, 20))) - return 0; - p += 16; /* SERIAL REFRESH RETRY EXPIRE */ - - GETLONG(ttl, p); /* minTTL */ - if (ttl < minttl) - minttl = ttl; + /* SOA must be for the name we're interested in. */ + if (soa_len <= name_len && memcmp(daemon->workspacename, name + name_len - soa_len, soa_len) == 0) + { + int prefix = name_len - soa_len; + + if (!no_cache) + { + if (!(addr.rrblock.rrdata = blockdata_alloc(NULL, 0))) + return 0; + addr.rrblock.rrtype = T_SOA; + addr.rrblock.datalen = 0; + } + + for (j = 0; j < 2; j++) /* MNAME, RNAME */ + { + if (!extract_name(header, qlen, &p, daemon->workspacename, 1, 0)) + { + if (!no_cache) + blockdata_free(addr.rrblock.rrdata); + return 0; + } + + if (!no_cache) + { + len = to_wire(daemon->workspacename); + if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, daemon->workspacename, len)) + { + blockdata_free(addr.rrblock.rrdata); + return 0; + } + + addr.rrblock.datalen += len; + } + } + + if (!CHECK_LEN(header, p, qlen, 20)) + { + if (!no_cache) + blockdata_free(addr.rrblock.rrdata); + return 0; + } + + /* rest of RR */ + if (!no_cache && !blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p, 20)) + { + blockdata_free(addr.rrblock.rrdata); + return 0; + } + + addr.rrblock.datalen += 20; + + if (!no_cache) + { + int secflag = 0; + +#ifdef HAVE_DNSSEC + if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[i + ntohs(header->ancount)] != 0) + { + secflag = F_DNSSECOK; + + /* limit TTL based on signature. */ + if (daemon->rr_status[i + ntohs(header->ancount)] < ttl) + ttl = daemon->rr_status[i + ntohs(header->ancount)]; + } +#endif + + if (!cache_insert(name + prefix, &addr, C_IN, now, ttl, F_FORWARD | F_RR | F_KEYTAG | secflag)) + { + blockdata_free(addr.rrblock.rrdata); + return 0; + } + } + + p += 16; /* SERIAL REFRESH RETRY EXPIRE */ + + GETLONG(minttl, p); /* minTTL */ + if (ttl < minttl) + minttl = ttl; + + if (substring) + *substring = prefix; + + if (ttlp) + *ttlp = minttl; + + return 1; + } } - else if (!ADD_RDLEN(header, p, qlen, rdlen)) + + p = psave; + + if (!ADD_RDLEN(header, p, qlen, rdlen)) return 0; /* bad packet */ } - /* rewrite addresses in additional section too */ - if (!do_doctor(p, ntohs(header->arcount), header, qlen, doctored)) - return 0; - - if (!found_soa) - minttl = daemon->neg_ttl; - - return minttl; + return 0; } /* Print TXT reply to log */ -static int print_txt(struct dns_header *header, const size_t qlen, char *name, - unsigned char *p, const int ardlen, int secflag) +static int log_txt(char *name, unsigned char *p, const int ardlen, int flag) { unsigned char *p1 = p; - if (!CHECK_LEN(header, p1, qlen, ardlen)) - return 0; + /* Loop over TXT payload */ while ((p1 - p) < ardlen) { @@ -519,14 +608,14 @@ /* make counted string zero-term and sanitise */ for (i = 0; i < len; i++) { - if (!isprint((int)*(p3+1))) + if (!isprint((unsigned char)*(p3+1))) break; *p3 = *(p3+1); p3++; } *p3 = 0; - log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, (char*)p1, 0); + log_query(flag, name, NULL, (char*)p1, 0); /* restore */ memmove(p1 + 1, p1, i); *p1 = len; @@ -543,10 +632,10 @@ */ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind, - int no_cache_dnssec, int secure, int *doctored) + int no_cache_dnssec, int secure) { unsigned char *p, *p1, *endrr, *namep; - int j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; + int j, qtype, qclass, aqtype, aqclass, ardlen, res; unsigned long ttl = 0; union all_addr addr; #ifdef HAVE_IPSET @@ -566,28 +655,9 @@ int cname_short = 0; #endif unsigned long cttl = ULONG_MAX, attl; - - cache_start_insert(); - /* find_soa is needed for dns_doctor side effects, so don't call it lazily if there are any. */ - if (daemon->doctors || option_bool(OPT_DNSSEC_VALID)) - { - searched_soa = 1; - ttl = find_soa(header, qlen, doctored); + cache_start_insert(); - if (*doctored) - { - if (secure) - return 0; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID)) - for (j = 0; j < ntohs(header->ancount); j++) - if (daemon->rr_status[j] != 0) - return 0; -#endif - } - } - namep = p = (unsigned char *)(header+1); if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4)) @@ -636,7 +706,7 @@ if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR)) { #ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && !no_cache_dnssec && daemon->rr_status[j] != 0) + if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0) { /* validated RR anywhere in CNAME chain, don't cache. */ if (cname_short || aqtype == T_CNAME) @@ -685,17 +755,16 @@ if (!found && !option_bool(OPT_NO_NEG)) { - if (!searched_soa) - { - searched_soa = 1; - ttl = find_soa(header, qlen, doctored); - } + /* For reverse records, we use the name field to store the SOA name. */ + int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now); flags |= F_NEG | (secure ? F_DNSSECOK : 0); if (name_encoding && ttl) { flags |= F_REVERSE | name_encoding; - cache_insert(NULL, &addr, C_IN, now, ttl, flags); + if (!have_soa) + flags |= F_NO_RR; /* Marks no SOA found. */ + cache_insert(name + substring, &addr, C_IN, now, ttl, flags); } log_query(flags | F_UPSTREAM, name, &addr, NULL, 0); @@ -717,8 +786,9 @@ addrlen = IN6ADDRSZ; flags |= F_IPV6; } - else if (qtype == T_SRV) - flags |= F_SRV; + else if (qtype != T_CNAME && + (qtype == T_SRV || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY))) + flags |= F_RR; else insert = 0; /* NOTE: do not cache data from CNAME queries. */ @@ -743,18 +813,19 @@ } GETSHORT(ardlen, p1); endrr = p1+ardlen; + + if (!CHECK_LEN(header, endrr, qlen, 0)) + return 2; /* bad packet */ /* Not what we're looking for? */ if (aqclass != C_IN || res == 2) { p1 = endrr; - if (!CHECK_LEN(header, p1, qlen, 0)) - return 2; /* bad packet */ continue; } #ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && !no_cache_dnssec && daemon->rr_status[j] != 0) + if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0) { secflag = F_DNSSECOK; @@ -799,36 +870,108 @@ found = 1; } - else if (aqtype != qtype) + else if (qtype == T_ANY || aqtype != qtype) { #ifdef HAVE_DNSSEC if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG) #endif - log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype); + log_query(secflag | F_FORWARD | F_UPSTREAM | F_RRNAME, name, NULL, NULL, aqtype); } else if (!(flags & F_NXDOMAIN)) { found = 1; - if (flags & F_SRV) + if (flags & F_RR) { + short desc, *rrdesc = rrfilter_desc(aqtype); unsigned char *tmp = namep; - if (!CHECK_LEN(header, p1, qlen, 6)) + if (!CHECK_LEN(header, p1, qlen, ardlen)) return 2; /* bad packet */ - GETSHORT(addr.srv.priority, p1); - GETSHORT(addr.srv.weight, p1); - GETSHORT(addr.srv.srvport, p1); - if (!extract_name(header, qlen, &p1, name, 1, 0)) - return 2; - addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */ - if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen))) - return 0; - /* we overwrote the original name, so get it back here. */ - if (!extract_name(header, qlen, &tmp, name, 1, 0)) - return 2; - } + /* If the data has no names and is small enough, store it in + the crec address field rather than allocate a block. */ + if (*rrdesc == -1 && ardlen <= (int)RR_IMDATALEN) + { + addr.rrdata.rrtype = aqtype; + addr.rrdata.datalen = (char)ardlen; + flags &= ~F_KEYTAG; /* in case of >1 answer, not all the same. */ + if (ardlen != 0) + memcpy(addr.rrdata.data, p1, ardlen); + } + else + { + addr.rrblock.rrtype = aqtype; + addr.rrblock.datalen = 0; + flags |= F_KEYTAG; /* discriminates between rrdata and rrblock */ + + /* The RR data may include names, and those names may include + compression, which will be rendered meaningless when + copied into another packet. + Here we go through a description of the packet type to + find the names, and extract them to a c-string and then + re-encode them to standalone DNS format without compression. */ + if (!(addr.rrblock.rrdata = blockdata_alloc(NULL, 0))) + return 0; + do + { + desc = *rrdesc++; + + if (desc == -1) + { + /* Copy the rest of the RR and end. */ + if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, endrr - p1)) + { + blockdata_free(addr.rrblock.rrdata); + return 0; + } + addr.rrblock.datalen += endrr - p1; + } + else if (desc == 0) + { + /* Name, extract it then re-encode. */ + int len; + + if (!extract_name(header, qlen, &p1, name, 1, 0)) + { + blockdata_free(addr.rrblock.rrdata); + return 2; + } + + len = to_wire(name); + if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, name, len)) + { + blockdata_free(addr.rrblock.rrdata); + return 0; + } + + addr.rrblock.datalen += len; + } + else + { + /* desc is length of a block of data to be used as-is */ + if (desc > endrr - p1) + desc = endrr - p1; + + if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, desc)) + { + blockdata_free(addr.rrblock.rrdata); + return 0; + } + + addr.rrblock.datalen += desc; + p1 += desc; + } + } while (desc != -1); + + /* we overwrote the original name, so get it back here. */ + if (!extract_name(header, qlen, &tmp, name, 1, 0)) + { + blockdata_free(addr.rrblock.rrdata); + return 2; + } + } + } else if (flags & (F_IPV4 | F_IPV6)) { /* copy address into aligned storage */ @@ -872,15 +1015,23 @@ cpp->addr.cname.uid = newc->uid; } cpp = NULL; + + /* cache insert failed, don't leak blockdata. */ + if (!newc && (flags & F_RR) && (flags & F_KEYTAG)) + blockdata_free(addr.rrblock.rrdata); } + /* We're filtering this RRtype. It will be removed from the + returned packet in process_reply() but gets cached here anyway + and will be filtered again on the way out of the cache. Here, + we just need to alter the logging. */ + if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype)) + secflag = F_NEG | F_CONFIG; + if (aqtype == T_TXT) - { - if (!print_txt(header, qlen, name, p1, ardlen, secflag)) - return 2; - } + log_txt(name, p1, ardlen, flags | F_FORWARD | F_UPSTREAM | secflag); else - log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype); + log_query(flags | F_FORWARD | F_UPSTREAM | secflag, name, &addr, NULL, aqtype); } p1 = endrr; @@ -892,29 +1043,33 @@ { if (flags & F_NXDOMAIN) { - flags &= ~(F_IPV4 | F_IPV6 | F_SRV); + flags &= ~(F_IPV4 | F_IPV6 | F_RR); - /* Can store NXDOMAIN reply to CNAME or ANY query. */ - if (qtype == T_CNAME || qtype == T_ANY) - insert = 1; + /* Can store NXDOMAIN reply for any qtype. */ + insert = 1; } log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL, 0); - if (!searched_soa) + if (insert && !option_bool(OPT_NO_NEG)) { - searched_soa = 1; - ttl = find_soa(header, qlen, doctored); - } - - /* If there's no SOA to get the TTL from, but there is a CNAME - pointing at this, inherit its TTL */ - if (insert && !option_bool(OPT_NO_NEG) && (ttl || cpp)) - { - if (ttl == 0) - ttl = cttl; + int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now); + + /* If there's no SOA to get the TTL from, but there is a CNAME + pointing at this, inherit its TTL */ + if (ttl || cpp) + { + if (!ttl) + ttl = cttl; + + addr.rrdata.datalen = substring; + addr.rrdata.rrtype = qtype; + + if (!have_soa) + flags |= F_NO_RR; /* Marks no SOA found. */ + } - newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); + newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0)); if (newc && cpp) { next_uid(newc); @@ -924,13 +1079,11 @@ } } } - - /* Don't put stuff from a truncated packet into the cache. - Don't cache replies from non-recursive nameservers, since we may get a + + /* Don't cache replies from non-recursive nameservers, since we may get a reply containing a CNAME but not its target, even though the target does exist. */ - if (!(header->hb3 & HB3_TC) && - !(header->hb4 & HB4_CD) && + if (!(header->hb4 & HB4_CD) && (header->hb4 & HB4_RA) && !no_cache_dnssec) cache_end_insert(); @@ -1134,6 +1287,10 @@ if (cache_find_non_terminal(name, now)) return 1; + if (is_name_synthetic(F_IPV4, name, NULL) || + is_name_synthetic(F_IPV6, name, NULL)) + return 1; + return 0; } @@ -1159,8 +1316,7 @@ GETSHORT(qtype, p); GETSHORT(qclass, p); GETLONG(ttl, p); - GETSHORT(rdlen, p); - + GETSHORT(rdlen, p) if (ttlp) *ttlp = ttl; @@ -1213,8 +1369,9 @@ /* Found a bogus address. Insert that info here, since there no SOA record to get the ttl from in the normal processing */ cache_start_insert(); - cache_insert(name, NULL, C_IN, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN); + cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | F_NXDOMAIN); cache_end_insert(); + log_query(F_CONFIG | F_FORWARD | F_NEG | F_NXDOMAIN, name, NULL, NULL, 0); return 1; } @@ -1409,7 +1566,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now, int ad_reqd, int do_bit, int have_pseudoheader, - int *stale) + int *stale, int *filtered) { char *name = daemon->namebuff; unsigned char *p, *ansp; @@ -1417,19 +1574,23 @@ union all_addr addr; int nameoffset; unsigned short flag; - int q, ans, anscount = 0, addncount = 0; - int dryrun = 0; - struct crec *crecp; + int ans, anscount = 0, nscount = 0, addncount = 0; + struct crec *crecp, *soa_lookup = NULL; int nxdomain = 0, notimp = 0, auth = 1, trunc = 0, sec_data = 1; struct mx_srv_record *rec; size_t len; int rd_bit = (header->hb3 & HB3_RD); - + int count = 255; /* catch loops */ + if (stale) *stale = 0; + + if (filtered) + *filtered = 0; /* never answer queries with RD unset, to avoid cache snooping. */ - if (ntohs(header->ancount) != 0 || + if ( ntohs(header->qdcount) != 1 || + ntohs(header->ancount) != 0 || ntohs(header->nscount) != 0 || ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY ) @@ -1439,16 +1600,9 @@ if (header->hb4 & HB4_CD) sec_data = 0; - /* If there is an additional data section then it will be overwritten by - partial replies, so we have to do a dry run to see if we can answer - the query. */ - if (ntohs(header->arcount) != 0) - dryrun = 1; - for (rec = daemon->mxnames; rec; rec = rec->next) rec->offset = 0; - rerun: /* determine end of question section (we put answers there) */ if (!(ansp = skip_questions(header, qlen))) return 0; /* bad packet */ @@ -1456,640 +1610,683 @@ /* now process each question, answers go in RRs after the question */ p = (unsigned char *)(header+1); - for (q = ntohs(header->qdcount); q != 0; q--) - { - int count = 255; /* catch loops */ - - /* save pointer to name for copying into answers */ - nameoffset = p - (unsigned char *)header; - - /* now extract name as .-concatenated string into name */ - if (!extract_name(header, qlen, &p, name, 1, 4)) - return 0; /* bad packet */ - - GETSHORT(qtype, p); - GETSHORT(qclass, p); - - ans = 0; /* have we answered this question */ - - if (qclass == C_IN) - while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME | F_NXDOMAIN))) + /* save pointer to name for copying into answers */ + nameoffset = p - (unsigned char *)header; + + /* now extract name as .-concatenated string into name */ + if (!extract_name(header, qlen, &p, name, 1, 4)) + return 0; /* bad packet */ + + GETSHORT(qtype, p); + GETSHORT(qclass, p); + + ans = 0; /* have we answered this question */ + + if (qclass == C_IN) + while (--count != 0 && (crecp = cache_find_by_name(NULL, name, now, F_CNAME | F_NXDOMAIN))) + { + char *cname_target; + int stale_flag = 0; + + if (crec_isstale(crecp, now)) { - char *cname_target; - int stale_flag = 0; + if (stale) + *stale = 1; - if (crec_isstale(crecp, now)) + stale_flag = F_STALE; + } + + if (crecp->flags & F_NEG) + soa_lookup = crecp; + + if (crecp->flags & F_NXDOMAIN) + { + if (qtype == T_CNAME) { - if (stale) - *stale = 1; - - stale_flag = F_STALE; + log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0); + auth = 0; + nxdomain = 1; + ans = 1; } + break; + } + + cname_target = cache_get_cname_target(crecp); + + /* If the client asked for DNSSEC don't use cached data. */ + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || + (rd_bit && (!do_bit || cache_validated(crecp)))) + { + if (crecp->flags & F_CONFIG || qtype == T_CNAME) + ans = 1; - if (crecp->flags & F_NXDOMAIN) - { - if (qtype == T_CNAME) - { - if (!dryrun) - log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0); - auth = 0; - nxdomain = 1; - ans = 1; - } - break; - } - - cname_target = cache_get_cname_target(crecp); + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; - /* If the client asked for DNSSEC don't use cached data. */ - if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || - (rd_bit && (!do_bit || cache_validated(crecp)))) + log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + crec_ttl(crecp, now), &nameoffset, + T_CNAME, C_IN, "d", cname_target)) + anscount++; + } + else + return 0; /* give up if any cached CNAME in chain can't be used for DNSSEC reasons. */ + + if (qtype == T_CNAME) + break; + + strcpy(name, cname_target); + } + + if (qtype == T_TXT || qtype == T_ANY) + { + struct txt_record *t; + for(t = daemon->txt; t ; t = t->next) + { + if (t->class == qclass && hostname_isequal(name, t->name)) + { + unsigned long ttl = daemon->local_ttl; + int ok = 1; + + ans = 1, sec_data = 0; +#ifndef NO_ID + /* Dynamically generate stat record */ + if (t->stat != 0) + { + ttl = 0; + if (!cache_make_stat(t)) + ok = 0; + } +#endif + if (ok) + { + log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + ttl, NULL, + T_TXT, t->class, "t", t->len, t->txt)) + anscount++; + } + } + } + } + + if (qclass == C_CHAOS) + { + /* don't forward *.bind and *.server chaos queries - always reply with NOTIMP */ + if (hostname_issubdomain("bind", name) || hostname_issubdomain("server", name)) + { + if (!ans) + { + notimp = 1, auth = 0; + + addr.log.rcode = NOTIMP; + log_query(F_CONFIG | F_RCODE, name, &addr, NULL, 0); + + ans = 1, sec_data = 0; + } + } + } + + if (qclass == C_IN) + { + struct txt_record *t; + + for (t = daemon->rr; t; t = t->next) + if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name)) + { + ans = 1; + sec_data = 0; + log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, t->class); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + daemon->local_ttl, NULL, + t->class, C_IN, "t", t->len, t->txt)) + anscount++; + } + + if (qtype == T_PTR || qtype == T_ANY) + { + /* see if it's w.z.y.z.in-addr.arpa format */ + int is_arpa = in_arpa_name_2_addr(name, &addr); + struct ptr_record *ptr; + struct interface_name* intr = NULL; + + for (ptr = daemon->ptr; ptr; ptr = ptr->next) + if (hostname_isequal(name, ptr->name)) + break; + + if (is_arpa == F_IPV4) + for (intr = daemon->int_names; intr; intr = intr->next) { - if (crecp->flags & F_CONFIG || qtype == T_CNAME) - ans = 1; + struct addrlist *addrlist; - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; + for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) + if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr4.s_addr == addrlist->addr.addr4.s_addr) + break; - if (!dryrun) - { - log_query(stale_flag | crecp->flags, name, NULL, record_source(crecp->uid), 0); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - crec_ttl(crecp, now), &nameoffset, - T_CNAME, C_IN, "d", cname_target)) - anscount++; - } + if (addrlist) + break; + else if (!(intr->flags & INP4)) + while (intr->next && strcmp(intr->intr, intr->next->intr) == 0) + intr = intr->next; + } + else if (is_arpa == F_IPV6) + for (intr = daemon->int_names; intr; intr = intr->next) + { + struct addrlist *addrlist; + + for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) + if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr6, &addrlist->addr.addr6)) + break; + if (addrlist) + break; + else if (!(intr->flags & INP6)) + while (intr->next && strcmp(intr->intr, intr->next->intr) == 0) + intr = intr->next; } - else - return 0; /* give up if any cached CNAME in chain can't be used for DNSSEC reasons. */ - - if (qtype == T_CNAME) - break; - - strcpy(name, cname_target); - } - - if (qtype == T_TXT || qtype == T_ANY) - { - struct txt_record *t; - for(t = daemon->txt; t ; t = t->next) + + if (intr) { - if (t->class == qclass && hostname_isequal(name, t->name)) - { - ans = 1, sec_data = 0; - if (!dryrun) - { - unsigned long ttl = daemon->local_ttl; - int ok = 1; -#ifndef NO_ID - /* Dynamically generate stat record */ - if (t->stat != 0) + sec_data = 0; + ans = 1; + log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + daemon->local_ttl, NULL, + T_PTR, C_IN, "d", intr->name)) + anscount++; + } + else if (ptr) + { + ans = 1; + sec_data = 0; + log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); + for (ptr = daemon->ptr; ptr; ptr = ptr->next) + if (hostname_isequal(name, ptr->name) && + add_resource_record(header, limit, &trunc, nameoffset, &ansp, + daemon->local_ttl, NULL, + T_PTR, C_IN, "d", ptr->ptr)) + anscount++; + + } + else if (is_arpa && (crecp = cache_find_by_addr(NULL, &addr, now, is_arpa))) + { + /* Don't use cache when DNSSEC data required, unless we know that + the zone is unsigned, which implies that we're doing + validation. */ + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || + (rd_bit && (!do_bit || cache_validated(crecp)) )) + { + do + { + int stale_flag = 0; + + if (crec_isstale(crecp, now)) { - ttl = 0; - if (!cache_make_stat(t)) - ok = 0; + if (stale) + *stale = 1; + + stale_flag = F_STALE; } -#endif - if (ok) + + /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */ + if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) + continue; + + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; + + ans = 1; + + if (crecp->flags & F_NEG) + { + auth = 0; + if (crecp->flags & F_NXDOMAIN) + nxdomain = 1; + log_query(stale_flag | (crecp->flags & ~F_FORWARD), name, &addr, NULL, 0); + soa_lookup = crecp; + } + else { - log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); + if (!(crecp->flags & (F_HOSTS | F_DHCP))) + auth = 0; + + log_query(stale_flag | (crecp->flags & ~F_FORWARD), cache_get_name(crecp), &addr, + record_source(crecp->uid), 0); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - ttl, NULL, - T_TXT, t->class, "t", t->len, t->txt)) + crec_ttl(crecp, now), NULL, + T_PTR, C_IN, "d", cache_get_name(crecp))) anscount++; } - } + } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa))); } } - } - - if (qclass == C_CHAOS) - { - /* don't forward *.bind and *.server chaos queries - always reply with NOTIMP */ - if (hostname_issubdomain("bind", name) || hostname_issubdomain("server", name)) + else if (is_rev_synth(is_arpa, &addr, name)) { - if (!ans) - { - notimp = 1, auth = 0; - if (!dryrun) - { - addr.log.rcode = NOTIMP; - log_query(F_CONFIG | F_RCODE, name, &addr, NULL, 0); - } - ans = 1, sec_data = 0; - } + ans = 1; + sec_data = 0; + log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL, 0); + + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + daemon->local_ttl, NULL, + T_PTR, C_IN, "d", name)) + anscount++; + } + else if (option_bool(OPT_BOGUSPRIV) && + ((is_arpa == F_IPV6 && private_net6(&addr.addr6, 1)) || (is_arpa == F_IPV4 && private_net(addr.addr4, 1))) && + !lookup_domain(name, F_DOMAINSRV, NULL, NULL)) + { + /* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */ + ans = 1; + sec_data = 0; + nxdomain = 1; + log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN, + name, &addr, NULL, 0); } } - - if (qclass == C_IN) + + for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0) { - struct txt_record *t; - - for (t = daemon->rr; t; t = t->next) - if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name)) - { - ans = 1; - sec_data = 0; - if (!dryrun) - { - log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, t->class); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - daemon->local_ttl, NULL, - t->class, C_IN, "t", t->len, t->txt)) - anscount++; - } - } - - if (qtype == T_PTR || qtype == T_ANY) + unsigned short type = (flag == F_IPV6) ? T_AAAA : T_A; + struct interface_name *intr; + + if (qtype != type && qtype != T_ANY) + continue; + + /* interface name stuff */ + for (intr = daemon->int_names; intr; intr = intr->next) + if (hostname_isequal(name, intr->name)) + break; + + if (intr) { - /* see if it's w.z.y.z.in-addr.arpa format */ - int is_arpa = in_arpa_name_2_addr(name, &addr); - struct ptr_record *ptr; - struct interface_name* intr = NULL; - - for (ptr = daemon->ptr; ptr; ptr = ptr->next) - if (hostname_isequal(name, ptr->name)) - break; - - if (is_arpa == F_IPV4) + struct addrlist *addrlist; + int gotit = 0, localise = 0; + + enumerate_interfaces(0); + + /* See if a putative address is on the network from which we received + the query, is so we'll filter other answers. */ + if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && type == T_A) for (intr = daemon->int_names; intr; intr = intr->next) - { - struct addrlist *addrlist; - + if (hostname_isequal(name, intr->name)) for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) - if (!(addrlist->flags & ADDRLIST_IPV6) && addr.addr4.s_addr == addrlist->addr.addr4.s_addr) - break; - - if (addrlist) - break; - else if (!(intr->flags & INP4)) - while (intr->next && strcmp(intr->intr, intr->next->intr) == 0) - intr = intr->next; - } - else if (is_arpa == F_IPV6) - for (intr = daemon->int_names; intr; intr = intr->next) + if (!(addrlist->flags & ADDRLIST_IPV6) && + is_same_net(addrlist->addr.addr4, local_addr, local_netmask)) + { + localise = 1; + break; + } + + for (intr = daemon->int_names; intr; intr = intr->next) + if (hostname_isequal(name, intr->name)) { - struct addrlist *addrlist; - for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) - if ((addrlist->flags & ADDRLIST_IPV6) && IN6_ARE_ADDR_EQUAL(&addr.addr6, &addrlist->addr.addr6)) - break; - - if (addrlist) - break; - else if (!(intr->flags & INP6)) - while (intr->next && strcmp(intr->intr, intr->next->intr) == 0) - intr = intr->next; - } - - if (intr) - { - sec_data = 0; - ans = 1; - if (!dryrun) - { - log_query(is_arpa | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - daemon->local_ttl, NULL, - T_PTR, C_IN, "d", intr->name)) - anscount++; - } - } - else if (ptr) - { - ans = 1; - sec_data = 0; - if (!dryrun) - { - log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); - for (ptr = daemon->ptr; ptr; ptr = ptr->next) - if (hostname_isequal(name, ptr->name) && - add_resource_record(header, limit, &trunc, nameoffset, &ansp, - daemon->local_ttl, NULL, - T_PTR, C_IN, "d", ptr->ptr)) - anscount++; - - } - } - else if (is_arpa && (crecp = cache_find_by_addr(NULL, &addr, now, is_arpa))) - { - /* Don't use cache when DNSSEC data required, unless we know that - the zone is unsigned, which implies that we're doing - validation. */ - if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || - (rd_bit && (!do_bit || cache_validated(crecp)) )) - { - do - { - int stale_flag = 0; - - if (crec_isstale(crecp, now)) - { - if (stale) - *stale = 1; - - stale_flag = F_STALE; - } - - /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */ - if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP))) + if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type) + { + if (localise && + !is_same_net(addrlist->addr.addr4, local_addr, local_netmask)) continue; + if (addrlist->flags & ADDRLIST_REVONLY) + continue; - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; - - ans = 1; - - if (crecp->flags & F_NEG) - { - auth = 0; - if (crecp->flags & F_NXDOMAIN) - nxdomain = 1; - if (!dryrun) - log_query(stale_flag | (crecp->flags & ~F_FORWARD), name, &addr, NULL, 0); - } - else - { - if (!(crecp->flags & (F_HOSTS | F_DHCP))) - auth = 0; - if (!dryrun) - { - log_query(stale_flag | (crecp->flags & ~F_FORWARD), cache_get_name(crecp), &addr, - record_source(crecp->uid), 0); - - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - crec_ttl(crecp, now), NULL, - T_PTR, C_IN, "d", cache_get_name(crecp))) - anscount++; - } - } - } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa))); - } - } - else if (is_rev_synth(is_arpa, &addr, name)) - { - ans = 1; - sec_data = 0; - if (!dryrun) - { - log_query(F_CONFIG | F_REVERSE | is_arpa, name, &addr, NULL, 0); - - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - daemon->local_ttl, NULL, - T_PTR, C_IN, "d", name)) - anscount++; - } - } - else if (option_bool(OPT_BOGUSPRIV) && - ((is_arpa == F_IPV6 && private_net6(&addr.addr6, 1)) || (is_arpa == F_IPV4 && private_net(addr.addr4, 1))) && - !lookup_domain(name, F_DOMAINSRV, NULL, NULL)) - { - /* if no configured server, not in cache, enabled and private IPV4 address, return NXDOMAIN */ - ans = 1; - sec_data = 0; - nxdomain = 1; - if (!dryrun) - log_query(F_CONFIG | F_REVERSE | is_arpa | F_NEG | F_NXDOMAIN, - name, &addr, NULL, 0); - } + ans = 1; + sec_data = 0; + gotit = 1; + log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + daemon->local_ttl, NULL, type, C_IN, + type == T_A ? "4" : "6", &addrlist->addr)) + anscount++; + } + } + + if (!gotit) + log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL, 0); + + continue; } - - for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0) + + if ((crecp = cache_find_by_name(NULL, name, now, flag))) { - unsigned short type = (flag == F_IPV6) ? T_AAAA : T_A; - struct interface_name *intr; - - if (qtype != type && qtype != T_ANY) - continue; + int localise = 0; - /* interface name stuff */ - for (intr = daemon->int_names; intr; intr = intr->next) - if (hostname_isequal(name, intr->name)) - break; + /* See if a putative address is on the network from which we received + the query, is so we'll filter other answers. */ + if (!(crecp->flags & F_NEG) && local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4) + { + struct crec *save = crecp; + do { + if ((crecp->flags & F_HOSTS) && + is_same_net(crecp->addr.addr4, local_addr, local_netmask)) + { + localise = 1; + break; + } + } while ((crecp = cache_find_by_name(crecp, name, now, flag))); + crecp = save; + } - if (intr) - { - struct addrlist *addrlist; - int gotit = 0, localise = 0; - - enumerate_interfaces(0); + /* If the client asked for DNSSEC don't use cached data. */ + if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || + (rd_bit && (!do_bit || cache_validated(crecp)) )) + do + { + int stale_flag = 0; - /* See if a putative address is on the network from which we received - the query, is so we'll filter other answers. */ - if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && type == T_A) - for (intr = daemon->int_names; intr; intr = intr->next) - if (hostname_isequal(name, intr->name)) - for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) - if (!(addrlist->flags & ADDRLIST_IPV6) && - is_same_net(addrlist->addr.addr4, local_addr, local_netmask)) - { - localise = 1; - break; - } - - for (intr = daemon->int_names; intr; intr = intr->next) - if (hostname_isequal(name, intr->name)) + if (crec_isstale(crecp, now)) { - for (addrlist = intr->addr; addrlist; addrlist = addrlist->next) - if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == type) - { - if (localise && - !is_same_net(addrlist->addr.addr4, local_addr, local_netmask)) - continue; - - if (addrlist->flags & ADDRLIST_REVONLY) - continue; - - ans = 1; - sec_data = 0; - if (!dryrun) - { - gotit = 1; - log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - daemon->local_ttl, NULL, type, C_IN, - type == T_A ? "4" : "6", &addrlist->addr)) - anscount++; - } - } - } - - if (!dryrun && !gotit) - log_query(F_FORWARD | F_CONFIG | flag | F_NEG, name, NULL, NULL, 0); - - continue; - } - - if ((crecp = cache_find_by_name(NULL, name, now, flag | F_NXDOMAIN | (dryrun ? F_NO_RR : 0)))) - { - int localise = 0; - - /* See if a putative address is on the network from which we received - the query, is so we'll filter other answers. */ - if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4) - { - struct crec *save = crecp; - do { - if ((crecp->flags & F_HOSTS) && - is_same_net(crecp->addr.addr4, local_addr, local_netmask)) - { - localise = 1; - break; - } - } while ((crecp = cache_find_by_name(crecp, name, now, flag))); - crecp = save; - } - - /* If the client asked for DNSSEC don't use cached data. */ - if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) || - (rd_bit && (!do_bit || cache_validated(crecp)) )) - do - { - int stale_flag = 0; + if (stale) + *stale = 1; - if (crec_isstale(crecp, now)) - { - if (stale) - *stale = 1; - - stale_flag = F_STALE; - } - - /* don't answer wildcard queries with data not from /etc/hosts - or DHCP leases */ - if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))) - break; + stale_flag = F_STALE; + } + + /* don't answer wildcard queries with data not from /etc/hosts + or DHCP leases */ + if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))) + break; + + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; + + if (!(crecp->flags & (F_HOSTS | F_DHCP))) + auth = 0; + + if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype) && + !(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG | F_NEG))) + { + /* We have a cached answer but we're filtering it. */ + ans = 1; + sec_data = 0; - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; + log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); - if (crecp->flags & F_NEG) + if (filtered) + *filtered = 1; + } + else if (crecp->flags & F_NEG) + { + if (qtype != T_ANY) { ans = 1; auth = 0; + soa_lookup = crecp; if (crecp->flags & F_NXDOMAIN) nxdomain = 1; - if (!dryrun) - log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); - } - else - { - /* If we are returning local answers depending on network, - filter here. */ - if (localise && - (crecp->flags & F_HOSTS) && - !is_same_net(crecp->addr.addr4, local_addr, local_netmask)) - continue; - if (!(crecp->flags & (F_HOSTS | F_DHCP))) - auth = 0; - - ans = 1; - if (!dryrun) - { - log_query(stale_flag | (crecp->flags & ~F_REVERSE), name, &crecp->addr, - record_source(crecp->uid), 0); - - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - crec_ttl(crecp, now), NULL, type, C_IN, - type == T_A ? "4" : "6", &crecp->addr)) - anscount++; - } + log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); } - } while ((crecp = cache_find_by_name(crecp, name, now, flag))); - } - else if (is_name_synthetic(flag, name, &addr)) - { - ans = 1, sec_data = 0; - if (!dryrun) - { - log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addr)) - anscount++; - } + } + else + { + /* If we are returning local answers depending on network, + filter here. */ + if (localise && + (crecp->flags & F_HOSTS) && + !is_same_net(crecp->addr.addr4, local_addr, local_netmask)) + continue; + + ans = 1; + log_query(stale_flag | (crecp->flags & ~F_REVERSE), name, &crecp->addr, + record_source(crecp->uid), 0); + + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + crec_ttl(crecp, now), NULL, type, C_IN, + type == T_A ? "4" : "6", &crecp->addr)) + anscount++; + } + } while ((crecp = cache_find_by_name(crecp, name, now, flag))); } - } - - if (qtype == T_MX || qtype == T_ANY) + else if (is_name_synthetic(flag, name, &addr)) { - int found = 0; - for (rec = daemon->mxnames; rec; rec = rec->next) - if (!rec->issrv && hostname_isequal(name, rec->name)) + ans = 1, sec_data = 0; + log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, + daemon->local_ttl, NULL, type, C_IN, type == T_A ? "4" : "6", &addr)) + anscount++; + } + } + + if (qtype == T_MX || qtype == T_ANY) + { + int found = 0; + for (rec = daemon->mxnames; rec; rec = rec->next) + if (!rec->issrv && hostname_isequal(name, rec->name)) + { + int offset; + + ans = found = 1; + sec_data = 0; + + log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, + &offset, T_MX, C_IN, "sd", rec->weight, rec->target)) { - ans = found = 1; - sec_data = 0; - if (!dryrun) - { - int offset; - log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, - &offset, T_MX, C_IN, "sd", rec->weight, rec->target)) - { - anscount++; - if (rec->target) - rec->offset = offset; - } - } + anscount++; + if (rec->target) + rec->offset = offset; } - - if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && - cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR)) - { - ans = 1; - sec_data = 0; - if (!dryrun) - { - log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, - T_MX, C_IN, "sd", 1, - option_bool(OPT_SELFMX) ? name : daemon->mxtarget)) - anscount++; - } - } + } + + if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) && + cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP | F_NO_RR)) + { + ans = 1; + sec_data = 0; + log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, + T_MX, C_IN, "sd", 1, + option_bool(OPT_SELFMX) ? name : daemon->mxtarget)) + anscount++; } - - if (qtype == T_SRV || qtype == T_ANY) + } + + if (qtype == T_SRV || qtype == T_ANY) + { + struct mx_srv_record *move = NULL, **up = &daemon->mxnames; + + for (rec = daemon->mxnames; rec; rec = rec->next) + if (rec->issrv && hostname_isequal(name, rec->name)) + { + int offset; + + ans = 1; + sec_data = 0; + log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, + &offset, T_SRV, C_IN, "sssd", + rec->priority, rec->weight, rec->srvport, rec->target)) + { + anscount++; + if (rec->target) + rec->offset = offset; + } + + /* unlink first SRV record found */ + if (!move) + { + move = rec; + *up = rec->next; + } + else + up = &rec->next; + } + else + up = &rec->next; + + /* put first SRV record back at the end. */ + if (move) { - int found = 0; - struct mx_srv_record *move = NULL, **up = &daemon->mxnames; - - for (rec = daemon->mxnames; rec; rec = rec->next) - if (rec->issrv && hostname_isequal(name, rec->name)) + *up = move; + move->next = NULL; + } + } + + if (qtype == T_NAPTR || qtype == T_ANY) + { + struct naptr *na; + for (na = daemon->naptr; na; na = na->next) + if (hostname_isequal(name, na->name)) + { + ans = 1; + sec_data = 0; + log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); + if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, + NULL, T_NAPTR, C_IN, "sszzzd", + na->order, na->pref, na->flags, na->services, na->regexp, na->replace)) + anscount++; + } + } + + if (qtype == T_MAILB) + ans = 1, nxdomain = 1, sec_data = 0; + + if (qtype == T_SOA && option_bool(OPT_FILTER)) + { + ans = 1; + sec_data = 0; + log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0); + } + + if (!ans) + { + if ((crecp = cache_find_by_name(NULL, name, now, F_RR | F_NXDOMAIN)) && + rd_bit && (!do_bit || cache_validated(crecp))) + do + { + int flags = crecp->flags; + unsigned short rrtype; + + if (flags & F_KEYTAG) + rrtype = crecp->addr.rrblock.rrtype; + else + rrtype = crecp->addr.rrdata.rrtype; + + if ((flags & F_NXDOMAIN) || rrtype == qtype) { - found = ans = 1; - sec_data = 0; - if (!dryrun) - { - int offset; - log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, - &offset, T_SRV, C_IN, "sssd", - rec->priority, rec->weight, rec->srvport, rec->target)) - { - anscount++; - if (rec->target) - rec->offset = offset; - } - } + char *rrdata = NULL; + unsigned short rrlen = 0; - /* unlink first SRV record found */ - if (!move) + if (crec_isstale(crecp, now)) { - move = rec; - *up = rec->next; + if (stale) + *stale = 1; + + flags |= F_STALE; } - else - up = &rec->next; - } - else - up = &rec->next; - - /* put first SRV record back at the end. */ - if (move) - { - *up = move; - move->next = NULL; - } + + if (!(flags & F_DNSSECOK)) + sec_data = 0; + + if (flags & F_NXDOMAIN) + nxdomain = 1; + else if (qtype != T_ANY && rr_on_list(daemon->filter_rr, qtype)) + flags |= F_NEG | F_CONFIG; + + auth = 0; + ans = 1; - if (!found) - { - if ((crecp = cache_find_by_name(NULL, name, now, F_SRV | F_NXDOMAIN | (dryrun ? F_NO_RR : 0))) && - rd_bit && (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK)))) - do + if (flags & F_NEG) + soa_lookup = crecp; + + if (!(flags & F_NEG)) { - int stale_flag = 0; - - if (crec_isstale(crecp, now)) + if (flags & F_KEYTAG) { - if (stale) - *stale = 1; - - stale_flag = F_STALE; + rrlen = crecp->addr.rrblock.datalen; + rrdata = blockdata_retrieve(crecp->addr.rrblock.rrdata, crecp->addr.rrblock.datalen, NULL); } - /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases, except for NXDOMAIN */ - if (qtype == T_ANY && !(crecp->flags & (F_NXDOMAIN))) - break; - - if (!(crecp->flags & F_DNSSECOK)) - sec_data = 0; - - auth = 0; - found = ans = 1; - - if (crecp->flags & F_NEG) + else { - if (crecp->flags & F_NXDOMAIN) - nxdomain = 1; - if (!dryrun) - log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); - } - else if (!dryrun) - { - char *target = blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, NULL); - log_query(stale_flag | crecp->flags, name, NULL, NULL, 0); - - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, - crec_ttl(crecp, now), NULL, T_SRV, C_IN, "sssd", - crecp->addr.srv.priority, crecp->addr.srv.weight, crecp->addr.srv.srvport, - target)) - anscount++; + rrlen = crecp->addr.rrdata.datalen; + rrdata = crecp->addr.rrdata.data; } - } while ((crecp = cache_find_by_name(crecp, name, now, F_SRV))); - } - - if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) - { - ans = 1; - sec_data = 0; - if (!dryrun) - log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0); - } - } - - if (qtype == T_NAPTR || qtype == T_ANY) - { - struct naptr *na; - for (na = daemon->naptr; na; na = na->next) - if (hostname_isequal(name, na->name)) - { - ans = 1; - sec_data = 0; - if (!dryrun) - { - log_query(F_CONFIG | F_RRNAME, name, NULL, "", 0); - if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, - NULL, T_NAPTR, C_IN, "sszzzd", - na->order, na->pref, na->flags, na->services, na->regexp, na->replace)) - anscount++; } + + if (!(flags & F_NEG) && add_resource_record(header, limit, &trunc, nameoffset, &ansp, + crec_ttl(crecp, now), NULL, qtype, C_IN, "t", + rrlen, rrdata)) + anscount++; + + /* log after cache insertion as log_txt mangles rrdata */ + if (qtype == T_TXT && !(crecp->flags & F_NEG)) + log_txt(name, (unsigned char *)rrdata, rrlen, crecp->flags & F_DNSSECOK); + else + log_query(flags, name, &crecp->addr, NULL, 0); } - } + } while ((crecp = cache_find_by_name(crecp, name, now, F_RR))); + } + + if (!ans && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_')))) + { + ans = 1; + sec_data = 0; + log_query(F_CONFIG | F_NEG, name, NULL, NULL, 0); + } + + + if (qtype != T_ANY && !ans && rr_on_list(daemon->filter_rr, qtype)) + { + /* We don't have a cached answer and when we get an answer from upstream we're going to + filter it anyway. If we have a cached answer for the domain for another RRtype then + that may be enough to tell us if the answer should be NODATA and save the round trip. + Cached NXDOMAIN has already been handled, so here we look for any record for the domain, + since its existence allows us to return a NODATA answer. Note that we never set the AD flag, + since we didn't authenticate the record. */ - if (qtype == T_MAILB) - ans = 1, nxdomain = 1, sec_data = 0; - - if (qtype == T_SOA && option_bool(OPT_FILTER)) + if (cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_RR | F_CNAME)) { ans = 1; - sec_data = 0; - if (!dryrun) - log_query(F_CONFIG | F_NEG, name, &addr, NULL, 0); + sec_data = auth = 0; + + log_query(F_NEG | F_CONFIG | flag, name, NULL, NULL, 0); + + if (filtered) + *filtered = 1; } } - - if (!ans) - return 0; /* failed to answer a question */ } - if (dryrun) + if (!ans) + return 0; /* failed to answer a question */ + + /* We found a negative record. See if we have an SOA record to + return in the AUTH section. + + For FORWARD NEG records, the addr.rrdata.datalen field of the othewise + empty addr is used to held an offset in to the name which yields the SOA + name. For REVERSE NEG records, the otherwise empty name field holds the + SOA name. If soa_name has zero length, then no SOA is known. soa_lookup + MUST be a neg record here. + + If the F_NO_RR flag is set, there was no SOA record supplied with the RR. */ + if (soa_lookup && !(soa_lookup->flags & F_NO_RR)) { - dryrun = 0; - goto rerun; + char *soa_name = soa_lookup->flags & F_REVERSE ? cache_get_name(soa_lookup) : name + soa_lookup->addr.rrdata.datalen; + + crecp = NULL; + while ((crecp = cache_find_by_name(crecp, soa_name, now, F_RR))) + if (crecp->addr.rrblock.rrtype == T_SOA) + { + char *rrdata; + + if (!(crecp->flags & F_NEG) && + (rrdata = blockdata_retrieve(crecp->addr.rrblock.rrdata, crecp->addr.rrblock.datalen, NULL)) && + add_resource_record(header, limit, &trunc, 0, &ansp, + crec_ttl(crecp, now), NULL, T_SOA, C_IN, "t", + soa_name, crecp->addr.rrblock.datalen, rrdata)) + { + nscount++; + + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; + } + break; + } } - + /* create an additional data section, for stuff in SRV and MX record replies. */ for (rec = daemon->mxnames; rec; rec = rec->next) if (rec->offset != 0) @@ -2111,7 +2308,11 @@ if (add_resource_record(header, limit, NULL, rec->offset, &ansp, crec_ttl(crecp, now), NULL, type, C_IN, crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr)) - addncount++; + { + addncount++; + if (!(crecp->flags & F_DNSSECOK)) + sec_data = 0; + } } } @@ -2136,7 +2337,7 @@ else SET_RCODE(header, NOERROR); /* no error */ header->ancount = htons(anscount); - header->nscount = htons(0); + header->nscount = htons(nscount); header->arcount = htons(addncount); len = ansp - (unsigned char *)header; diff -Nru dnsmasq-2.89/src/rfc2131.c dnsmasq-2.90/src/rfc2131.c --- dnsmasq-2.89/src/rfc2131.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/rfc2131.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1678,7 +1678,7 @@ for (i = option_len(opt); i > 0; i--) { char c = *p++; - if (isprint((int)c)) + if (isprint((unsigned char)c)) *buf++ = c; } *buf = 0; /* add terminator */ diff -Nru dnsmasq-2.89/src/rfc3315.c dnsmasq-2.90/src/rfc3315.c --- dnsmasq-2.89/src/rfc3315.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/rfc3315.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -333,12 +333,29 @@ else if (msg_type != DHCP6IREQ) return 0; - /* server-id must match except for SOLICIT, CONFIRM and REBIND messages */ - if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ && msg_type != DHCP6REBIND && - (!(opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1)) || - opt6_len(opt) != daemon->duid_len || - memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0)) - return 0; + /* server-id must match except for SOLICIT, CONFIRM and REBIND messages, which MUST NOT + have a server-id. 3315 para 15.x */ + opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1); + + if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND) + { + if (opt) + return 0; + } + else if (msg_type == DHCP6IREQ) + { + /* If server-id provided, it must match. */ + if (opt && (opt6_len(opt) != daemon->duid_len || + memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0)) + return 0; + } + else + { + /* Everything else MUST have a server-id that matches ours. */ + if (!opt || opt6_len(opt) != daemon->duid_len || + memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0) + return 0; + } o = new_opt6(OPTION6_SERVER_ID); put_opt6(daemon->duid, daemon->duid_len); @@ -353,7 +370,7 @@ put_opt6_short(DHCP6USEMULTI); put_opt6_string("Use multicast"); end_opt6(o1); - return 1; + goto done; } /* match vendor and user class options */ @@ -457,6 +474,8 @@ state->tags = &mac_opt->netid; } } + else if (option_bool(OPT_LOG_OPTS)) + my_syslog(MS_DHCP | LOG_INFO, _("%u cannot determine client MAC address"), state->xid); if ((opt = opt6_find(state->packet_options, state->end, OPTION6_FQDN, 1))) { @@ -1055,7 +1074,7 @@ case DHCP6CONFIRM: { - int good_addr = 0; + int good_addr = 0, bad_addr = 0; /* set reply message type */ outmsgtype = DHCP6REPLY; @@ -1077,32 +1096,35 @@ if (!address6_valid(state->context, &req_addr, tagif, 1)) { - o1 = new_opt6(OPTION6_STATUS_CODE); - put_opt6_short(DHCP6NOTONLINK); - put_opt6_string(_("confirm failed")); - end_opt6(o1); + bad_addr = 1; log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed")); - return 1; } - - good_addr = 1; - log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); + else + { + good_addr = 1; + log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname); + } } } /* No addresses, no reply: RFC 3315 18.2.2 */ - if (!good_addr) + if (!good_addr && !bad_addr) return 0; o1 = new_opt6(OPTION6_STATUS_CODE); - put_opt6_short(DHCP6SUCCESS ); - put_opt6_string(_("all addresses still on link")); + put_opt6_short(bad_addr ? DHCP6NOTONLINK : DHCP6SUCCESS); + put_opt6_string(bad_addr ? (_("confirm failed")) : (_("all addresses still on link"))); end_opt6(o1); break; } case DHCP6IREQ: { + /* 3315 para 15.12 */ + if (opt6_find(state->packet_options, state->end, OPTION6_IA_NA, 1) || + opt6_find(state->packet_options, state->end, OPTION6_IA_TA, 1)) + return 0; + /* We can't discriminate contexts based on address, as we don't know it. If there is only one possible context, we can use its tags */ if (state->context && state->context->netid.net && !state->context->current) @@ -1277,12 +1299,14 @@ } + log_tags(tagif, state->xid); + + done: /* Fill in the message type. Note that we store the offset, not a direct pointer, since the packet memory may have been reallocated. */ ((unsigned char *)(daemon->outpacket.iov_base))[start_msg] = outmsgtype; - log_tags(tagif, state->xid); log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1)); return 1; diff -Nru dnsmasq-2.89/src/rrfilter.c dnsmasq-2.90/src/rrfilter.c --- dnsmasq-2.89/src/rrfilter.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/rrfilter.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -136,9 +136,9 @@ if (class == C_IN) { - u16 *d; + short *d; - for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++) + for (pp = p, d = rrfilter_desc(type); *d != -1; d++) { if (*d != 0) pp += *d; @@ -156,41 +156,46 @@ } -/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. */ -size_t rrfilter(struct dns_header *header, size_t plen, int mode) +/* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. + * returns number of modified records. */ +size_t rrfilter(struct dns_header *header, size_t *plen, int mode) { static unsigned char **rrs = NULL; static int rr_sz = 0; unsigned char *p = (unsigned char *)(header+1); - int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar; + size_t rr_found = 0; + int i, rdlen, qtype, qclass, chop_an, chop_ns, chop_ar; + if (mode == RRFILTER_CONF && !daemon->filter_rr) + return 0; + if (ntohs(header->qdcount) != 1 || - !(p = skip_name(p, header, plen, 4))) - return plen; + !(p = skip_name(p, header, *plen, 4))) + return 0; GETSHORT(qtype, p); GETSHORT(qclass, p); /* First pass, find pointers to start and end of all the records we wish to elide: records added for DNSSEC, unless explicitly queried for */ - for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0; + for (chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++) { unsigned char *pstart = p; int type, class; - if (!(p = skip_name(p, header, plen, 10))) - return plen; + if (!(p = skip_name(p, header, *plen, 10))) + return rr_found; GETSHORT(type, p); GETSHORT(class, p); p += 4; /* TTL */ GETSHORT(rdlen, p); - if (!ADD_RDLEN(header, p, plen, rdlen)) - return plen; + if (!ADD_RDLEN(header, p, *plen, rdlen)) + return rr_found; if (mode == RRFILTER_EDNS0) /* EDNS */ { @@ -208,6 +213,14 @@ if (i < ntohs(header->ancount) && type == qtype && class == qclass) continue; } + else if (qtype == T_ANY && rr_on_list(daemon->filter_rr, T_ANY)) + { + /* Filter replies to ANY queries in the spirit of + RFC RFC 8482 para 4.3 */ + if (class != C_IN || + type == T_A || type == T_AAAA || type == T_MX || type == T_CNAME) + continue; + } else { /* Only looking at answer section now. */ @@ -217,15 +230,12 @@ if (class != C_IN) continue; - if (mode == RRFILTER_A && type != T_A) - continue; - - if (mode == RRFILTER_AAAA && type != T_AAAA) + if (!rr_on_list(daemon->filter_rr, type)) continue; } if (!expand_workspace(&rrs, &rr_sz, rr_found + 1)) - return plen; + return rr_found; rrs[rr_found++] = pstart; rrs[rr_found++] = p; @@ -240,7 +250,7 @@ /* Nothing to do. */ if (rr_found == 0) - return plen; + return rr_found; /* Second pass, look for pointers in names in the records we're keeping and make sure they don't point to records we're going to elide. This is theoretically possible, but unlikely. If @@ -248,42 +258,42 @@ p = (unsigned char *)(header+1); /* question first */ - if (!check_name(&p, header, plen, 0, rrs, rr_found)) - return plen; + if (!check_name(&p, header, *plen, 0, rrs, rr_found)) + return rr_found; p += 4; /* qclass, qtype */ /* Now answers and NS */ - if (!check_rrs(p, header, plen, 0, rrs, rr_found)) - return plen; + if (!check_rrs(p, header, *plen, 0, rrs, rr_found)) + return rr_found; /* Third pass, actually fix up pointers in the records */ p = (unsigned char *)(header+1); - check_name(&p, header, plen, 1, rrs, rr_found); + check_name(&p, header, *plen, 1, rrs, rr_found); p += 4; /* qclass, qtype */ - check_rrs(p, header, plen, 1, rrs, rr_found); + check_rrs(p, header, *plen, 1, rrs, rr_found); /* Fourth pass, elide records */ - for (p = rrs[0], i = 1; i < rr_found; i += 2) + for (p = rrs[0], i = 1; (unsigned)i < rr_found; i += 2) { unsigned char *start = rrs[i]; - unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen; + unsigned char *end = ((unsigned)i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + *plen; memmove(p, start, end-start); p += end-start; } - plen = p - (unsigned char *)header; + *plen = p - (unsigned char *)header; header->ancount = htons(ntohs(header->ancount) - chop_an); header->nscount = htons(ntohs(header->nscount) - chop_ns); header->arcount = htons(ntohs(header->arcount) - chop_ar); - return plen; + return rr_found; } /* This is used in the DNSSEC code too, hence it's exported */ -u16 *rrfilter_desc(int type) +short *rrfilter_desc(int type) { /* List of RRtypes which include domains in the data. 0 -> domain @@ -294,7 +304,7 @@ anything which needs no mangling. */ - static u16 rr_desc[] = + static short rr_desc[] = { T_NS, 0, -1, T_MD, 0, -1, @@ -319,10 +329,10 @@ 0, -1 /* wildcard/catchall */ }; - u16 *p = rr_desc; + short *p = rr_desc; while (*p != type && *p != 0) - while (*p++ != (u16)-1); + while (*p++ != -1); return p+1; } @@ -350,3 +360,78 @@ return 1; } + +/* Convert from presentation format to wire format, in place. + Also map UC -> LC. + Note that using extract_name to get presentation format + then calling to_wire() removes compression and maps case, + thus generating names in canonical form. + Calling to_wire followed by from_wire is almost an identity, + except that the UC remains mapped to LC. + + Note that both /000 and '.' are allowed within labels. These get + represented in presentation format using NAME_ESCAPE as an escape + character. In theory, if all the characters in a name were /000 or + '.' or NAME_ESCAPE then all would have to be escaped, so the + presentation format would be twice as long as the spec (1024). + The buffers are all declared as 2049 (allowing for the trailing zero) + for this reason. +*/ +int to_wire(char *name) +{ + unsigned char *l, *p, *q, term; + int len; + + for (l = (unsigned char*)name; *l != 0; l = p) + { + for (p = l; *p != '.' && *p != 0; p++) + if (*p >= 'A' && *p <= 'Z') + *p = *p - 'A' + 'a'; + else if (*p == NAME_ESCAPE) + { + for (q = p; *q; q++) + *q = *(q+1); + (*p)--; + } + term = *p; + + if ((len = p - l) != 0) + memmove(l+1, l, len); + *l = len; + + p++; + + if (term == 0) + *p = 0; + } + + return l + 1 - (unsigned char *)name; +} + +/* Note: no compression allowed in input. */ +void from_wire(char *name) +{ + unsigned char *l, *p, *last; + int len; + + for (last = (unsigned char *)name; *last != 0; last += *last+1); + + for (l = (unsigned char *)name; *l != 0; l += len+1) + { + len = *l; + memmove(l, l+1, len); + for (p = l; p < l + len; p++) + if (*p == '.' || *p == 0 || *p == NAME_ESCAPE) + { + memmove(p+1, p, 1 + last - p); + len++; + *p++ = NAME_ESCAPE; + (*p)++; + } + + l[len] = '.'; + } + + if ((char *)l != name) + *(l-1) = 0; +} diff -Nru dnsmasq-2.89/src/slaac.c dnsmasq-2.90/src/slaac.c --- dnsmasq-2.89/src/slaac.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/slaac.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/tftp.c dnsmasq-2.90/src/tftp.c --- dnsmasq-2.89/src/tftp.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/tftp.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -228,7 +228,8 @@ #ifdef HAVE_DHCP /* allowed interfaces are the same as for DHCP */ for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) - if (tmp->name && wildcard_match(tmp->name, name)) + if (tmp->name && (tmp->flags & INAME_4) && (tmp->flags & INAME_6) && + wildcard_match(tmp->name, name)) return; #endif } @@ -405,7 +406,7 @@ if (*p == '\\') *p = '/'; else if (option_bool(OPT_TFTP_LC)) - *p = tolower(*p); + *p = tolower((unsigned char)*p); strcpy(daemon->namebuff, "/"); if (prefix) @@ -584,8 +585,13 @@ void check_tftp_listeners(time_t now) { + struct listener *listener; struct tftp_transfer *transfer, *tmp, **up; + for (listener = daemon->listeners; listener; listener = listener->next) + if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN)) + tftp_request(listener, now); + /* In single port mode, all packets come via port 69 and tftp_request() */ if (!option_bool(OPT_SINGLE_PORT)) for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) diff -Nru dnsmasq-2.89/src/ubus.c dnsmasq-2.90/src/ubus.c --- dnsmasq-2.89/src/ubus.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/ubus.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru dnsmasq-2.89/src/util.c dnsmasq-2.90/src/util.c --- dnsmasq-2.89/src/util.c 2023-01-13 21:57:01.000000000 +0000 +++ dnsmasq-2.90/src/util.c 2024-02-13 13:49:15.000000000 +0000 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -115,6 +115,19 @@ return (u64)out[outleft+1] + (((u64)out[outleft]) << 32); } +int rr_on_list(struct rrlist *list, unsigned short rr) +{ + while (list) + { + if (list->rr == rr) + return 1; + + list = list->next; + } + + return 0; +} + /* returns 1 if name is OK and ascii printable * returns 2 if name should be processed by IDN */ static int check_name(char *in) @@ -280,11 +293,9 @@ if (limit && p + 1 > (unsigned char*)limit) return NULL; -#ifdef HAVE_DNSSEC - if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE) + if (*sval == NAME_ESCAPE) *p++ = (*(++sval))-1; else -#endif *p++ = *sval; }